camstack 1.2.28 → 1.2.29
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.
|
@@ -23631,9 +23631,9 @@ var require_zod = __commonJS({
|
|
|
23631
23631
|
}
|
|
23632
23632
|
});
|
|
23633
23633
|
|
|
23634
|
-
// ../system/dist/dist-
|
|
23635
|
-
var
|
|
23636
|
-
"../system/dist/dist-
|
|
23634
|
+
// ../system/dist/dist-DR1VmGz6.js
|
|
23635
|
+
var require_dist_DR1VmGz6 = __commonJS({
|
|
23636
|
+
"../system/dist/dist-DR1VmGz6.js"(exports) {
|
|
23637
23637
|
"use strict";
|
|
23638
23638
|
var zod = require_zod();
|
|
23639
23639
|
var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
|
|
@@ -23650,6 +23650,7 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
23650
23650
|
EventCategory2["AddonInstalled"] = "addon.installed";
|
|
23651
23651
|
EventCategory2["AddonUninstalled"] = "addon.uninstalled";
|
|
23652
23652
|
EventCategory2["AddonCrashed"] = "addon.crashed";
|
|
23653
|
+
EventCategory2["AddonRunnerFailed"] = "addon.runner-failed";
|
|
23653
23654
|
EventCategory2["AddonError"] = "addon.error";
|
|
23654
23655
|
EventCategory2["AddonPageReady"] = "addon.page-ready";
|
|
23655
23656
|
EventCategory2["AddonWidgetReady"] = "addon.widget-ready";
|
|
@@ -25931,6 +25932,10 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
25931
25932
|
preBufferSec: zod.z.number().min(0).optional(),
|
|
25932
25933
|
postBufferSec: zod.z.number().min(0).optional()
|
|
25933
25934
|
});
|
|
25935
|
+
({
|
|
25936
|
+
preBufferSec: 10,
|
|
25937
|
+
postBufferSec: 30
|
|
25938
|
+
}).postBufferSec * 1e3;
|
|
25934
25939
|
var RecordingRetentionSchema = zod.z.object({
|
|
25935
25940
|
maxAgeDays: zod.z.number().min(0).optional(),
|
|
25936
25941
|
maxSizeGb: zod.z.number().min(0).optional()
|
|
@@ -25947,6 +25952,12 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
25947
25952
|
/** DERIVED summary of `bands`, stamped by the recorder on every save.
|
|
25948
25953
|
* Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
|
|
25949
25954
|
mode: RecordingStorageModeSchema.optional(),
|
|
25955
|
+
/**
|
|
25956
|
+
* Which assigned broker slots to record. Absent / empty = {@link
|
|
25957
|
+
* DEFAULT_RECORDING_PROFILES} (`high`+`low`) intersected with the
|
|
25958
|
+
* camera's currently assigned slots — never `mid` unless the operator
|
|
25959
|
+
* picks it, and never a slot the broker has not assigned.
|
|
25960
|
+
*/
|
|
25950
25961
|
profiles: zod.z.array(CamProfileSchema).optional(),
|
|
25951
25962
|
segmentSeconds: zod.z.number().int().positive().optional(),
|
|
25952
25963
|
/**
|
|
@@ -26009,7 +26020,11 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
26009
26020
|
profiles: zod.z.array(zod.z.string()).optional(),
|
|
26010
26021
|
/** Copy throttle in MB/s (default 40) — the drain is a background chore,
|
|
26011
26022
|
* never allowed to starve live writers. */
|
|
26012
|
-
throttleMbps: zod.z.number().min(1).max(1e3).optional()
|
|
26023
|
+
throttleMbps: zod.z.number().min(1).max(1e3).optional(),
|
|
26024
|
+
/** Move only segments whose startMs is >= this. Absent = the whole source
|
|
26025
|
+
* pile. Used when a full drain is too expensive and the operator only
|
|
26026
|
+
* wants the recent window on the new disk. */
|
|
26027
|
+
sinceMs: zod.z.number().int().optional()
|
|
26013
26028
|
});
|
|
26014
26029
|
var StorageMigrationLeaseInputSchema = zod.z.object({ leaseId: zod.z.string().min(1) });
|
|
26015
26030
|
var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: zod.z.string().min(1) });
|
|
@@ -33092,6 +33107,7 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
33092
33107
|
"node-offline",
|
|
33093
33108
|
"node-inference-unavailable",
|
|
33094
33109
|
"detection-blind",
|
|
33110
|
+
"addon-crash-loop",
|
|
33095
33111
|
"addon-update-available",
|
|
33096
33112
|
"server-update-available",
|
|
33097
33113
|
"alarm-triggered",
|
|
@@ -33099,6 +33115,9 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
33099
33115
|
"alarm-disarmed",
|
|
33100
33116
|
"alarm-arming",
|
|
33101
33117
|
"alarm-arm-refused",
|
|
33118
|
+
"addon-updated",
|
|
33119
|
+
"server-updated",
|
|
33120
|
+
"export-completed",
|
|
33102
33121
|
"camera-online",
|
|
33103
33122
|
"camera-offline",
|
|
33104
33123
|
"camera-disabled",
|
|
@@ -34512,7 +34531,9 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
34512
34531
|
* `'staging'`. */
|
|
34513
34532
|
markForTrain: zod.z.boolean().optional(),
|
|
34514
34533
|
/** Operator marked this track for diagnostic attention. */
|
|
34515
|
-
debug: zod.z.boolean().optional()
|
|
34534
|
+
debug: zod.z.boolean().optional(),
|
|
34535
|
+
/** Operator favourited this track. Pins it against pruning. */
|
|
34536
|
+
favourited: zod.z.boolean().optional()
|
|
34516
34537
|
};
|
|
34517
34538
|
var TrackRetrainFields = { retrainStatus: RetrainStatusSchema.optional() };
|
|
34518
34539
|
var TrackFlagsPatchSchema = zod.z.object(TrackFlagFields);
|
|
@@ -34520,6 +34541,7 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
34520
34541
|
trackId: zod.z.string(),
|
|
34521
34542
|
markForTrain: zod.z.boolean(),
|
|
34522
34543
|
debug: zod.z.boolean(),
|
|
34544
|
+
favourited: zod.z.boolean(),
|
|
34523
34545
|
/** The lifecycle state the boolean was derived from. Required here (unlike on
|
|
34524
34546
|
* a track row) because this shape is only ever produced by the write body,
|
|
34525
34547
|
* which always knows it — and a surface that has just written needs to render
|
|
@@ -35042,6 +35064,11 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
35042
35064
|
/** Per-track CLIP search vectors removed (best-effort). */
|
|
35043
35065
|
embeddings: zod.z.number().int()
|
|
35044
35066
|
});
|
|
35067
|
+
var DiskReconcileCountsSchema = zod.z.object({
|
|
35068
|
+
mediaDropped: zod.z.number().int(),
|
|
35069
|
+
tracks: zod.z.number().int(),
|
|
35070
|
+
events: zod.z.number().int()
|
|
35071
|
+
});
|
|
35045
35072
|
var EventStoreDeviceFootprintSchema = zod.z.object({
|
|
35046
35073
|
deviceId: zod.z.number(),
|
|
35047
35074
|
/** Persisted event rows (motion + object + audio) for the camera. */
|
|
@@ -35298,6 +35325,17 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
35298
35325
|
auth: "admin"
|
|
35299
35326
|
}),
|
|
35300
35327
|
/**
|
|
35328
|
+
* Disk-wins reconcile for one camera. Drops media index rows whose blobs
|
|
35329
|
+
* are gone, then cascades tracks (including favourited and staging) that
|
|
35330
|
+
* have no remaining files. Enrolled identity/vehicle/scene media is never
|
|
35331
|
+
* probed. Trackless motion/audio events with no remaining file are dropped,
|
|
35332
|
+
* including snapshot-less rows.
|
|
35333
|
+
*/
|
|
35334
|
+
reconcileFromDisk: method(zod.z.object({ deviceId: zod.z.number() }), DiskReconcileCountsSchema, {
|
|
35335
|
+
kind: "mutation",
|
|
35336
|
+
auth: "admin"
|
|
35337
|
+
}),
|
|
35338
|
+
/**
|
|
35301
35339
|
* Delete whole tracks (object events) by id for the given device,
|
|
35302
35340
|
* cascading their media in lockstep. Returns the number of tracks
|
|
35303
35341
|
* actually deleted plus the ids that could not be removed.
|
|
@@ -35316,7 +35354,7 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
35316
35354
|
auth: "admin"
|
|
35317
35355
|
}),
|
|
35318
35356
|
/**
|
|
35319
|
-
* Set the per-track operator flags (`markForTrain`, `debug`) on ONE track.
|
|
35357
|
+
* Set the per-track operator flags (`markForTrain`, `debug`, `favourited`) on ONE track.
|
|
35320
35358
|
* The patch is PARTIAL — an omitted key is left untouched — because the
|
|
35321
35359
|
* three surfaces that write it (admin Events grid, viewer track detail,
|
|
35322
35360
|
* viewer cluster detail) each own one toggle and must not clobber the other.
|
|
@@ -37161,6 +37199,24 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
37161
37199
|
/** Wall-clock ms spent on the stage before it was abandoned. */
|
|
37162
37200
|
elapsedMs: zod.z.number()
|
|
37163
37201
|
});
|
|
37202
|
+
var DiskReconcileJobSchema = zod.z.object({
|
|
37203
|
+
state: zod.z.enum([
|
|
37204
|
+
"idle",
|
|
37205
|
+
"running",
|
|
37206
|
+
"done",
|
|
37207
|
+
"error"
|
|
37208
|
+
]),
|
|
37209
|
+
total: zod.z.number().int().nonnegative(),
|
|
37210
|
+
completed: zod.z.number().int().nonnegative(),
|
|
37211
|
+
currentDeviceId: zod.z.number().int().nullable(),
|
|
37212
|
+
failed: zod.z.array(zod.z.number().int()).readonly(),
|
|
37213
|
+
mediaDropped: zod.z.number().int().nonnegative(),
|
|
37214
|
+
tracks: zod.z.number().int().nonnegative(),
|
|
37215
|
+
events: zod.z.number().int().nonnegative(),
|
|
37216
|
+
startedAtMs: zod.z.number().int().nullable(),
|
|
37217
|
+
finishedAtMs: zod.z.number().int().nullable(),
|
|
37218
|
+
error: zod.z.string().nullable()
|
|
37219
|
+
});
|
|
37164
37220
|
var CameraStatusSchema = zod.z.object({
|
|
37165
37221
|
deviceId: zod.z.number(),
|
|
37166
37222
|
assignment: CameraAssignmentStatusSchema,
|
|
@@ -37685,6 +37741,18 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
37685
37741
|
* rail without issuing N parallel browser round-trips.
|
|
37686
37742
|
*/
|
|
37687
37743
|
getCameraStatuses: method(zod.z.object({ deviceIds: zod.z.array(zod.z.number()).optional() }), zod.z.array(CameraStatusSchema).readonly()),
|
|
37744
|
+
/**
|
|
37745
|
+
* Disk-wins fleet reconcile after a recordings wipe. Starts the walk in
|
|
37746
|
+
* the addon process and returns immediately with the job snapshot — the
|
|
37747
|
+
* work outlives the tRPC/UDS 60s timeout. Poll `getReconcileFromDiskStatus`.
|
|
37748
|
+
* Admin-only. Idempotent while a job is already running.
|
|
37749
|
+
*/
|
|
37750
|
+
reconcileFromDisk: method(zod.z.void(), DiskReconcileJobSchema, {
|
|
37751
|
+
kind: "mutation",
|
|
37752
|
+
auth: "admin"
|
|
37753
|
+
}),
|
|
37754
|
+
/** Snapshot of the in-flight or last disk-wins fleet reconcile. */
|
|
37755
|
+
getReconcileFromDiskStatus: method(zod.z.void(), DiskReconcileJobSchema, { auth: "admin" }),
|
|
37688
37756
|
/** List every template the operator has saved. */
|
|
37689
37757
|
listTemplates: method(zod.z.void(), zod.z.array(PipelineTemplateSchema).readonly()),
|
|
37690
37758
|
/** Create a new named preset from a given CameraPipelineConfig. */
|
|
@@ -38823,8 +38891,57 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
38823
38891
|
startMs: zod.z.number(),
|
|
38824
38892
|
endMs: zod.z.number()
|
|
38825
38893
|
}),
|
|
38826
|
-
/**
|
|
38827
|
-
thumbnail
|
|
38894
|
+
/**
|
|
38895
|
+
* Lazy thumbnail URL, never inlined.
|
|
38896
|
+
*
|
|
38897
|
+
* Recording-derived clips (events-mode keep-window, and the prepared
|
|
38898
|
+
* continuous event+fragment visit) MUST use the snapshot of the **main
|
|
38899
|
+
* event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
|
|
38900
|
+
* of the event that owns `kind` (object > motion > audio). Do not extract
|
|
38901
|
+
* a keyframe from the recorded segments. Other providers (onboard, HKSV)
|
|
38902
|
+
* mint their own stills.
|
|
38903
|
+
*
|
|
38904
|
+
* **VOUCHED, never fabricated.** A provider emits this only for an event it
|
|
38905
|
+
* has CONFIRMED owns at least one media row (its own, or its owning track's).
|
|
38906
|
+
* Absent is meaningful — "this visit has no event still" — never "we did not
|
|
38907
|
+
* look". Stamping it from a URL template made 35% of one camera's clips point
|
|
38908
|
+
* at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
|
|
38909
|
+
* A read that FAILS drops the claim; it never invents it.
|
|
38910
|
+
*/
|
|
38911
|
+
thumbnail: zod.z.string().optional(),
|
|
38912
|
+
/**
|
|
38913
|
+
* An instant INSIDE this visit's footage, hole-safe, where a recorded still
|
|
38914
|
+
* can be decoded. Present whenever the visit came from recorded availability;
|
|
38915
|
+
* absent on a per-event padded window (there is no footage to promise).
|
|
38916
|
+
*
|
|
38917
|
+
* This is not a thumbnail and not a second byte path: it is the argument to
|
|
38918
|
+
* the recorder's existing still route. The surface — never the provider —
|
|
38919
|
+
* decides whether to use it. It is the midpoint of the visit's LONGEST
|
|
38920
|
+
* contiguous range, not of the visit: a visit spans its holes by
|
|
38921
|
+
* construction, so a naive midpoint lands in dead air.
|
|
38922
|
+
*/
|
|
38923
|
+
stillAtMs: zod.z.number().optional(),
|
|
38924
|
+
/**
|
|
38925
|
+
* Analytics event ids that overlap this visit — a BOUNDED sample, newest
|
|
38926
|
+
* first within kind (object → motion → audio), capped at
|
|
38927
|
+
* {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
|
|
38928
|
+
*
|
|
38929
|
+
* Bounded because it is not a payload the surface pages through: one visit on
|
|
38930
|
+
* device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
|
|
38931
|
+
* camera-day. Read {@link eventCount} for the true total.
|
|
38932
|
+
*/
|
|
38933
|
+
eventIds: zod.z.array(zod.z.string()).optional(),
|
|
38934
|
+
/** How many analytics events actually overlap this visit. Differs from
|
|
38935
|
+
* `eventIds.length` exactly when the sample was capped — so a truncated
|
|
38936
|
+
* list is never mistaken for a quiet visit. */
|
|
38937
|
+
eventCount: zod.z.number().int().nonnegative().optional(),
|
|
38938
|
+
/** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
|
|
38939
|
+
* than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
|
|
38940
|
+
* bar keeps showing them via `recording.getAvailability`. */
|
|
38941
|
+
holes: zod.z.array(zod.z.object({
|
|
38942
|
+
startMs: zod.z.number(),
|
|
38943
|
+
endMs: zod.z.number()
|
|
38944
|
+
})).optional()
|
|
38828
38945
|
});
|
|
38829
38946
|
var ClipPlaybackSchema = zod.z.object({
|
|
38830
38947
|
/** HLS master URL through the hub data-plane (Range + token in path). */
|
|
@@ -39372,7 +39489,14 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
39372
39489
|
});
|
|
39373
39490
|
var AutoUpdateSettingsSchema = zod.z.object({
|
|
39374
39491
|
channel: ChannelSchema,
|
|
39375
|
-
intervalSeconds: zod.z.number()
|
|
39492
|
+
intervalSeconds: zod.z.number(),
|
|
39493
|
+
/**
|
|
39494
|
+
* Cadence of the "an update exists" POLLER, in seconds. Independent of
|
|
39495
|
+
* `channel`: the poller runs while auto-apply is `off`, because being told
|
|
39496
|
+
* about a publish and installing it are different decisions. Clamped
|
|
39497
|
+
* server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
|
|
39498
|
+
*/
|
|
39499
|
+
updateCheckIntervalSeconds: zod.z.number()
|
|
39376
39500
|
});
|
|
39377
39501
|
var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
39378
39502
|
var RestartAddonResultSchema = zod.z.unknown();
|
|
@@ -39623,7 +39747,9 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
39623
39747
|
getAutoUpdateSettings: method(zod.z.void(), AutoUpdateSettingsSchema, { auth: "admin" }),
|
|
39624
39748
|
setAutoUpdateSettings: method(zod.z.object({
|
|
39625
39749
|
channel: ChannelSchema,
|
|
39626
|
-
intervalSeconds: zod.z.number().min(300).max(86400).optional()
|
|
39750
|
+
intervalSeconds: zod.z.number().min(300).max(86400).optional(),
|
|
39751
|
+
/** Availability-poll cadence; see AutoUpdateSettingsSchema. */
|
|
39752
|
+
updateCheckIntervalSeconds: zod.z.number().min(900).max(604800).optional()
|
|
39627
39753
|
}), zod.z.unknown(), {
|
|
39628
39754
|
kind: "mutation",
|
|
39629
39755
|
auth: "admin"
|
|
@@ -44905,7 +45031,9 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
44905
45031
|
includeAudio: zod.z.boolean(),
|
|
44906
45032
|
maxLifeMs: zod.z.number().int().positive(),
|
|
44907
45033
|
deleteAfterDownload: zod.z.boolean(),
|
|
44908
|
-
title: zod.z.string().max(200).optional()
|
|
45034
|
+
title: zod.z.string().max(200).optional(),
|
|
45035
|
+
/** Notification-output target ids to ping when this export becomes ready. */
|
|
45036
|
+
notifyTargetIds: zod.z.array(zod.z.string().min(1)).max(20).optional()
|
|
44909
45037
|
}).superRefine((v, ctx) => {
|
|
44910
45038
|
if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
|
|
44911
45039
|
code: zod.z.ZodIssueCode.custom,
|
|
@@ -44959,14 +45087,23 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
44959
45087
|
scope: "system",
|
|
44960
45088
|
mode: "singleton",
|
|
44961
45089
|
methods: {
|
|
44962
|
-
/** Queue a render of `[fromMs,toMs)` for `deviceId`/`
|
|
44963
|
-
*
|
|
45090
|
+
/** Queue a render of `[fromMs,toMs)` for `deviceId`/`profiles` (legacy
|
|
45091
|
+
* singular `profile` still accepted). Fails fast when no footage covers
|
|
45092
|
+
* the range. One job per profile; returns the first queued record. */
|
|
44964
45093
|
createExport: method(zod.z.object({
|
|
44965
45094
|
deviceId: zod.z.number(),
|
|
44966
|
-
|
|
45095
|
+
/** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
|
|
45096
|
+
profile: zod.z.string().optional(),
|
|
45097
|
+
profiles: zod.z.array(zod.z.string()).min(1).optional(),
|
|
44967
45098
|
fromMs: zod.z.number(),
|
|
44968
45099
|
toMs: zod.z.number(),
|
|
44969
45100
|
options: ExportOptionsSchema
|
|
45101
|
+
}).superRefine((v, ctx) => {
|
|
45102
|
+
if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
|
|
45103
|
+
code: zod.z.ZodIssueCode.custom,
|
|
45104
|
+
message: "pass profiles[] (min 1) or legacy profile",
|
|
45105
|
+
path: ["profiles"]
|
|
45106
|
+
});
|
|
44970
45107
|
}), ExportRecordSchema, {
|
|
44971
45108
|
kind: "mutation",
|
|
44972
45109
|
auth: "protected"
|
|
@@ -50603,6 +50740,12 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
50603
50740
|
addonId: null,
|
|
50604
50741
|
access: "create"
|
|
50605
50742
|
},
|
|
50743
|
+
"pipelineAnalytics.reconcileFromDisk": {
|
|
50744
|
+
capName: "pipeline-analytics",
|
|
50745
|
+
capScope: "device",
|
|
50746
|
+
addonId: null,
|
|
50747
|
+
access: "create"
|
|
50748
|
+
},
|
|
50606
50749
|
"pipelineAnalytics.refreshStorageLocationsForMigration": {
|
|
50607
50750
|
capName: "pipeline-analytics",
|
|
50608
50751
|
capScope: "device",
|
|
@@ -51005,6 +51148,12 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
51005
51148
|
addonId: null,
|
|
51006
51149
|
access: "view"
|
|
51007
51150
|
},
|
|
51151
|
+
"pipelineOrchestrator.getReconcileFromDiskStatus": {
|
|
51152
|
+
capName: "pipeline-orchestrator",
|
|
51153
|
+
capScope: "system",
|
|
51154
|
+
addonId: null,
|
|
51155
|
+
access: "view"
|
|
51156
|
+
},
|
|
51008
51157
|
"pipelineOrchestrator.listAgentSettings": {
|
|
51009
51158
|
capName: "pipeline-orchestrator",
|
|
51010
51159
|
capScope: "system",
|
|
@@ -51029,6 +51178,12 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
51029
51178
|
addonId: null,
|
|
51030
51179
|
access: "create"
|
|
51031
51180
|
},
|
|
51181
|
+
"pipelineOrchestrator.reconcileFromDisk": {
|
|
51182
|
+
capName: "pipeline-orchestrator",
|
|
51183
|
+
capScope: "system",
|
|
51184
|
+
addonId: null,
|
|
51185
|
+
access: "create"
|
|
51186
|
+
},
|
|
51032
51187
|
"pipelineOrchestrator.removeAgentSettings": {
|
|
51033
51188
|
capName: "pipeline-orchestrator",
|
|
51034
51189
|
capScope: "system",
|
|
@@ -54020,6 +54175,11 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
54020
54175
|
form: "single",
|
|
54021
54176
|
optional: true
|
|
54022
54177
|
}],
|
|
54178
|
+
"pipelineAnalytics.reconcileFromDisk": [{
|
|
54179
|
+
name: "deviceId",
|
|
54180
|
+
form: "single",
|
|
54181
|
+
optional: false
|
|
54182
|
+
}],
|
|
54023
54183
|
"pipelineAnalytics.restageRetrainTrack": [{
|
|
54024
54184
|
name: "deviceId",
|
|
54025
54185
|
form: "single",
|
|
@@ -55207,6 +55367,8 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
55207
55367
|
function isSameAddonId(a, b) {
|
|
55208
55368
|
return bareAddonId(a) === bareAddonId(b);
|
|
55209
55369
|
}
|
|
55370
|
+
var MB = 1024 * 1024;
|
|
55371
|
+
1024 * MB, 3072 * MB;
|
|
55210
55372
|
function decodeVectorBase64(encoded) {
|
|
55211
55373
|
const buffer = Buffer.from(encoded, "base64");
|
|
55212
55374
|
if (buffer.byteLength % 4 !== 0) throw new Error(`decodeVectorBase64: ${buffer.byteLength} bytes is not a whole number of Float32 values`);
|
|
@@ -55913,7 +56075,7 @@ var require_alerts_addon = __commonJS({
|
|
|
55913
56075
|
[Symbol.toStringTag]: { value: "Module" }
|
|
55914
56076
|
});
|
|
55915
56077
|
require_chunk_Cek0wNdY();
|
|
55916
|
-
var require_dist10 =
|
|
56078
|
+
var require_dist10 = require_dist_DR1VmGz6();
|
|
55917
56079
|
function selectExpired(alerts, cutoffMs) {
|
|
55918
56080
|
return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
|
|
55919
56081
|
}
|
|
@@ -56726,7 +56888,7 @@ var require_console_logging = __commonJS({
|
|
|
56726
56888
|
[Symbol.toStringTag]: { value: "Module" }
|
|
56727
56889
|
});
|
|
56728
56890
|
require_chunk_Cek0wNdY();
|
|
56729
|
-
var require_dist10 =
|
|
56891
|
+
var require_dist10 = require_dist_DR1VmGz6();
|
|
56730
56892
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
56731
56893
|
var LEVEL_RANK = {
|
|
56732
56894
|
debug: 0,
|
|
@@ -56820,7 +56982,7 @@ var require_core_blocks_addon = __commonJS({
|
|
|
56820
56982
|
"use strict";
|
|
56821
56983
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
56822
56984
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
56823
|
-
var require_dist10 =
|
|
56985
|
+
var require_dist10 = require_dist_DR1VmGz6();
|
|
56824
56986
|
var node_crypto = __require("crypto");
|
|
56825
56987
|
var node_fs_promises = __require("fs/promises");
|
|
56826
56988
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -59540,7 +59702,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
59540
59702
|
[Symbol.toStringTag]: { value: "Module" }
|
|
59541
59703
|
});
|
|
59542
59704
|
require_chunk_Cek0wNdY();
|
|
59543
|
-
var require_dist10 =
|
|
59705
|
+
var require_dist10 = require_dist_DR1VmGz6();
|
|
59544
59706
|
var node_crypto = __require("crypto");
|
|
59545
59707
|
var _camstack_types_node = require_node();
|
|
59546
59708
|
var JOB_HISTORY = 20;
|
|
@@ -63528,7 +63690,7 @@ var require_hub_forwarder = __commonJS({
|
|
|
63528
63690
|
[Symbol.toStringTag]: { value: "Module" }
|
|
63529
63691
|
});
|
|
63530
63692
|
require_chunk_Cek0wNdY();
|
|
63531
|
-
var require_dist10 =
|
|
63693
|
+
var require_dist10 = require_dist_DR1VmGz6();
|
|
63532
63694
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
63533
63695
|
var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
|
|
63534
63696
|
var HubForwarderDestination = class {
|
|
@@ -63665,7 +63827,7 @@ var require_liveness_monitor_addon = __commonJS({
|
|
|
63665
63827
|
"use strict";
|
|
63666
63828
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
63667
63829
|
require_chunk_Cek0wNdY();
|
|
63668
|
-
var require_dist10 =
|
|
63830
|
+
var require_dist10 = require_dist_DR1VmGz6();
|
|
63669
63831
|
var NO_DEVICES = "liveness:no-devices";
|
|
63670
63832
|
var ALL_OFFLINE = "liveness:all-devices-offline";
|
|
63671
63833
|
var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
|
|
@@ -63855,7 +64017,7 @@ var require_local_auth_addon = __commonJS({
|
|
|
63855
64017
|
[Symbol.toStringTag]: { value: "Module" }
|
|
63856
64018
|
});
|
|
63857
64019
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
63858
|
-
var require_dist10 =
|
|
64020
|
+
var require_dist10 = require_dist_DR1VmGz6();
|
|
63859
64021
|
var node_crypto = __require("crypto");
|
|
63860
64022
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
63861
64023
|
var crypto$1 = __require("crypto");
|
|
@@ -71539,7 +71701,7 @@ var require_loki_logging = __commonJS({
|
|
|
71539
71701
|
[Symbol.toStringTag]: { value: "Module" }
|
|
71540
71702
|
});
|
|
71541
71703
|
require_chunk_Cek0wNdY();
|
|
71542
|
-
var require_dist10 =
|
|
71704
|
+
var require_dist10 = require_dist_DR1VmGz6();
|
|
71543
71705
|
function sanitizeLabelName(raw) {
|
|
71544
71706
|
const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
|
|
71545
71707
|
return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
|
|
@@ -72104,7 +72266,7 @@ var require_native_metrics_addon = __commonJS({
|
|
|
72104
72266
|
[Symbol.toStringTag]: { value: "Module" }
|
|
72105
72267
|
});
|
|
72106
72268
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
72107
|
-
var require_dist10 =
|
|
72269
|
+
var require_dist10 = require_dist_DR1VmGz6();
|
|
72108
72270
|
var node_child_process = __require("child_process");
|
|
72109
72271
|
var node_util = __require("util");
|
|
72110
72272
|
var node_os = __require("os");
|
|
@@ -73046,7 +73208,7 @@ var require_filesystem_storage_addon = __commonJS({
|
|
|
73046
73208
|
[Symbol.toStringTag]: { value: "Module" }
|
|
73047
73209
|
});
|
|
73048
73210
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
73049
|
-
var require_dist10 =
|
|
73211
|
+
var require_dist10 = require_dist_DR1VmGz6();
|
|
73050
73212
|
var node_crypto = __require("crypto");
|
|
73051
73213
|
var node_fs_promises = __require("fs/promises");
|
|
73052
73214
|
var node_path = __require("path");
|
|
@@ -74162,7 +74324,7 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74162
74324
|
[Symbol.toStringTag]: { value: "Module" }
|
|
74163
74325
|
});
|
|
74164
74326
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
74165
|
-
var require_dist10 =
|
|
74327
|
+
var require_dist10 = require_dist_DR1VmGz6();
|
|
74166
74328
|
var node_crypto = __require("crypto");
|
|
74167
74329
|
var node_fs = __require("fs");
|
|
74168
74330
|
var node_module = __require("module");
|
|
@@ -75730,7 +75892,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
75730
75892
|
[Symbol.toStringTag]: { value: "Module" }
|
|
75731
75893
|
});
|
|
75732
75894
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
75733
|
-
var require_dist10 =
|
|
75895
|
+
var require_dist10 = require_dist_DR1VmGz6();
|
|
75734
75896
|
var node_crypto = __require("crypto");
|
|
75735
75897
|
var node_fs_promises = __require("fs/promises");
|
|
75736
75898
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -77609,7 +77771,7 @@ var require_system_config_addon = __commonJS({
|
|
|
77609
77771
|
[Symbol.toStringTag]: { value: "Module" }
|
|
77610
77772
|
});
|
|
77611
77773
|
require_chunk_Cek0wNdY();
|
|
77612
|
-
var require_dist10 =
|
|
77774
|
+
var require_dist10 = require_dist_DR1VmGz6();
|
|
77613
77775
|
var SECTION_TITLES = {
|
|
77614
77776
|
server: "Server",
|
|
77615
77777
|
auth: "Authentication"
|
|
@@ -95670,7 +95832,7 @@ var require_winston_logging = __commonJS({
|
|
|
95670
95832
|
[Symbol.toStringTag]: { value: "Module" }
|
|
95671
95833
|
});
|
|
95672
95834
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
95673
|
-
var require_dist10 =
|
|
95835
|
+
var require_dist10 = require_dist_DR1VmGz6();
|
|
95674
95836
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
95675
95837
|
var node_path = __require("path");
|
|
95676
95838
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -96658,9 +96820,9 @@ var require_file_data_plane_DUHPHa_Y = __commonJS({
|
|
|
96658
96820
|
}
|
|
96659
96821
|
});
|
|
96660
96822
|
|
|
96661
|
-
// ../types/dist/event-category-
|
|
96662
|
-
var
|
|
96663
|
-
"../types/dist/event-category-
|
|
96823
|
+
// ../types/dist/event-category-CRPORAAz.js
|
|
96824
|
+
var require_event_category_CRPORAAz = __commonJS({
|
|
96825
|
+
"../types/dist/event-category-CRPORAAz.js"(exports) {
|
|
96664
96826
|
"use strict";
|
|
96665
96827
|
var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
|
|
96666
96828
|
EventCategory2["SystemBoot"] = "system.boot";
|
|
@@ -96676,6 +96838,7 @@ var require_event_category_D3gG7oil = __commonJS({
|
|
|
96676
96838
|
EventCategory2["AddonInstalled"] = "addon.installed";
|
|
96677
96839
|
EventCategory2["AddonUninstalled"] = "addon.uninstalled";
|
|
96678
96840
|
EventCategory2["AddonCrashed"] = "addon.crashed";
|
|
96841
|
+
EventCategory2["AddonRunnerFailed"] = "addon.runner-failed";
|
|
96679
96842
|
EventCategory2["AddonError"] = "addon.error";
|
|
96680
96843
|
EventCategory2["AddonPageReady"] = "addon.page-ready";
|
|
96681
96844
|
EventCategory2["AddonWidgetReady"] = "addon.widget-ready";
|
|
@@ -96823,11 +96986,11 @@ var require_event_category_D3gG7oil = __commonJS({
|
|
|
96823
96986
|
}
|
|
96824
96987
|
});
|
|
96825
96988
|
|
|
96826
|
-
// ../types/dist/sleep-
|
|
96827
|
-
var
|
|
96828
|
-
"../types/dist/sleep-
|
|
96989
|
+
// ../types/dist/sleep-CMRLJj2e.js
|
|
96990
|
+
var require_sleep_CMRLJj2e = __commonJS({
|
|
96991
|
+
"../types/dist/sleep-CMRLJj2e.js"(exports) {
|
|
96829
96992
|
"use strict";
|
|
96830
|
-
var require_event_category =
|
|
96993
|
+
var require_event_category = require_event_category_CRPORAAz();
|
|
96831
96994
|
var zod = require_zod();
|
|
96832
96995
|
var WELL_KNOWN_TABS = [
|
|
96833
96996
|
{
|
|
@@ -99298,6 +99461,7 @@ var require_sleep_EYtyUX0L = __commonJS({
|
|
|
99298
99461
|
pruneEventsBefore: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "pruneEventsBefore", "mutation", input),
|
|
99299
99462
|
pruneTracksBefore: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "pruneTracksBefore", "mutation", input),
|
|
99300
99463
|
wipeAllAnalytics: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "wipeAllAnalytics", "mutation", input),
|
|
99464
|
+
reconcileFromDisk: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "reconcileFromDisk", "mutation", input),
|
|
99301
99465
|
deleteTracks: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "deleteTracks", "mutation", input),
|
|
99302
99466
|
setTrackFlags: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "setTrackFlags", "mutation", input),
|
|
99303
99467
|
getEventStoreFootprint: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getEventStoreFootprint", "query", input),
|
|
@@ -99600,10 +99764,7 @@ var require_sleep_EYtyUX0L = __commonJS({
|
|
|
99600
99764
|
getDeviceLiveContribution: (input) => dispatchSystem("recording", "getDeviceLiveContribution", "query", input),
|
|
99601
99765
|
applyDeviceSettingsPatch: (input) => dispatchSystem("recording", "applyDeviceSettingsPatch", "mutation", input)
|
|
99602
99766
|
},
|
|
99603
|
-
recordingExport: {
|
|
99604
|
-
createExport: (input) => dispatchSystem("recordingExport", "createExport", "mutation", input),
|
|
99605
|
-
listExports: (input) => dispatchSystem("recordingExport", "listExports", "query", input)
|
|
99606
|
-
},
|
|
99767
|
+
recordingExport: { listExports: (input) => dispatchSystem("recordingExport", "listExports", "query", input) },
|
|
99607
99768
|
streamBroker: {
|
|
99608
99769
|
publishCameraStream: (input) => dispatchSystem("streamBroker", "publishCameraStream", "mutation", input),
|
|
99609
99770
|
retractCameraStream: (input) => dispatchSystem("streamBroker", "retractCameraStream", "mutation", input),
|
|
@@ -100266,8 +100427,8 @@ var require_addon = __commonJS({
|
|
|
100266
100427
|
"../types/dist/addon.js"(exports) {
|
|
100267
100428
|
"use strict";
|
|
100268
100429
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
100269
|
-
var require_event_category =
|
|
100270
|
-
var require_sleep =
|
|
100430
|
+
var require_event_category = require_event_category_CRPORAAz();
|
|
100431
|
+
var require_sleep = require_sleep_CMRLJj2e();
|
|
100271
100432
|
var require_err_msg = require_err_msg_COpsHMw2();
|
|
100272
100433
|
var CAP_INPUT_DEFAULTS = Object.freeze({
|
|
100273
100434
|
"addons": { "getLogs": { "limit": 100 } },
|
|
@@ -107141,9 +107302,9 @@ var require_dist2 = __commonJS({
|
|
|
107141
107302
|
}
|
|
107142
107303
|
});
|
|
107143
107304
|
|
|
107144
|
-
// ../system/dist/manifest-python-deps-
|
|
107145
|
-
var
|
|
107146
|
-
"../system/dist/manifest-python-deps-
|
|
107305
|
+
// ../system/dist/manifest-python-deps-GejnH--L.js
|
|
107306
|
+
var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
107307
|
+
"../system/dist/manifest-python-deps-GejnH--L.js"(exports) {
|
|
107147
107308
|
"use strict";
|
|
107148
107309
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
107149
107310
|
var node_crypto = __require("crypto");
|
|
@@ -107159,9 +107320,139 @@ var require_manifest_python_deps_BtVjvfvj = __commonJS({
|
|
|
107159
107320
|
var node_fs = __require("fs");
|
|
107160
107321
|
node_fs = require_chunk.__toESM(node_fs);
|
|
107161
107322
|
var node_http = __require("http");
|
|
107323
|
+
var node_v8 = __require("v8");
|
|
107324
|
+
node_v8 = require_chunk.__toESM(node_v8);
|
|
107325
|
+
var node_vm = __require("vm");
|
|
107326
|
+
node_vm = require_chunk.__toESM(node_vm);
|
|
107162
107327
|
var _camstack_types_addon = require_addon();
|
|
107163
107328
|
var _trpc_client = require_dist2();
|
|
107164
107329
|
var node_net = __require("net");
|
|
107330
|
+
var HEAP_WATCH_INTERVAL_MS = 6e4;
|
|
107331
|
+
var HEAP_WATCH_WARN_RATIO = 0.8;
|
|
107332
|
+
var HEAP_WATCH_ESCALATE_RATIO = 0.7;
|
|
107333
|
+
var HEAP_WATCH_DEESCALATE_RATIO = 0.6;
|
|
107334
|
+
function nextMode(current, usedRatio, escalateRatio = HEAP_WATCH_ESCALATE_RATIO, deescalateRatio = HEAP_WATCH_DEESCALATE_RATIO) {
|
|
107335
|
+
if (current === "escalated") return usedRatio < deescalateRatio ? "steady" : "escalated";
|
|
107336
|
+
return usedRatio >= escalateRatio ? "escalated" : "steady";
|
|
107337
|
+
}
|
|
107338
|
+
var HEAP_RECLAIM_TRIGGER_MB = 1024;
|
|
107339
|
+
var HEAP_RECLAIM_MIN_INTERVAL_MS = 12e4;
|
|
107340
|
+
var MB = (bytes) => Math.round(bytes / 1048576);
|
|
107341
|
+
function buildHeapSample(mem, heapLimitBytes, warnRatio = HEAP_WATCH_WARN_RATIO) {
|
|
107342
|
+
const usedRatio = heapLimitBytes > 0 ? mem.heapUsed / heapLimitBytes : 0;
|
|
107343
|
+
return {
|
|
107344
|
+
rssMb: MB(mem.rss),
|
|
107345
|
+
heapUsedMb: MB(mem.heapUsed),
|
|
107346
|
+
heapTotalMb: MB(mem.heapTotal),
|
|
107347
|
+
heapLimitMb: MB(heapLimitBytes),
|
|
107348
|
+
externalMb: MB(mem.external),
|
|
107349
|
+
arrayBuffersMb: MB(mem.arrayBuffers),
|
|
107350
|
+
usedRatio: Math.round(usedRatio * 100) / 100,
|
|
107351
|
+
nearLimit: usedRatio >= warnRatio
|
|
107352
|
+
};
|
|
107353
|
+
}
|
|
107354
|
+
var consoleSink = {
|
|
107355
|
+
info: (line) => console.log(line),
|
|
107356
|
+
warn: (line) => console.warn(line)
|
|
107357
|
+
};
|
|
107358
|
+
function strandedMb(s) {
|
|
107359
|
+
return s.rssMb - s.heapUsedMb - s.externalMb;
|
|
107360
|
+
}
|
|
107361
|
+
function shouldReclaim(s, triggerMb = HEAP_RECLAIM_TRIGGER_MB) {
|
|
107362
|
+
return strandedMb(s) > triggerMb;
|
|
107363
|
+
}
|
|
107364
|
+
function isGcFunction(value) {
|
|
107365
|
+
return typeof value === "function";
|
|
107366
|
+
}
|
|
107367
|
+
function createV8Reclaimer() {
|
|
107368
|
+
try {
|
|
107369
|
+
node_v8.setFlagsFromString("--expose-gc");
|
|
107370
|
+
const gc = node_vm.runInNewContext("gc");
|
|
107371
|
+
node_v8.setFlagsFromString("--no-expose-gc");
|
|
107372
|
+
if (!isGcFunction(gc)) return void 0;
|
|
107373
|
+
return () => gc({
|
|
107374
|
+
execution: "sync",
|
|
107375
|
+
flavor: "last-resort"
|
|
107376
|
+
});
|
|
107377
|
+
} catch {
|
|
107378
|
+
return;
|
|
107379
|
+
}
|
|
107380
|
+
}
|
|
107381
|
+
function format2(label, s) {
|
|
107382
|
+
return `[mem] ${label} rss=${s.rssMb}MB heapUsed=${s.heapUsedMb}MB heapTotal=${s.heapTotalMb}MB heapLimit=${s.heapLimitMb}MB used=${Math.round(s.usedRatio * 100)}% external=${s.externalMb}MB arrayBuffers=${s.arrayBuffersMb}MB`;
|
|
107383
|
+
}
|
|
107384
|
+
function startHeapWatch(label = "hub-main", sink = consoleSink, intervalMs = HEAP_WATCH_INTERVAL_MS, reclaimOptions) {
|
|
107385
|
+
const readMemory = reclaimOptions?.readMemory ?? (() => process.memoryUsage());
|
|
107386
|
+
const now = reclaimOptions?.now ?? (() => Date.now());
|
|
107387
|
+
const triggerMb = reclaimOptions?.triggerMb ?? 1024;
|
|
107388
|
+
const minIntervalMs = reclaimOptions?.minIntervalMs ?? 12e4;
|
|
107389
|
+
const fastIntervalMs = reclaimOptions?.fastIntervalMs ?? 2e3;
|
|
107390
|
+
const escalateRatio = reclaimOptions?.escalateRatio ?? 0.7;
|
|
107391
|
+
const deescalateRatio = reclaimOptions?.deescalateRatio ?? 0.6;
|
|
107392
|
+
let lastReclaimAt = Number.NEGATIVE_INFINITY;
|
|
107393
|
+
const read = () => {
|
|
107394
|
+
const limit = reclaimOptions?.heapLimitBytes ?? node_v8.getHeapStatistics().heap_size_limit;
|
|
107395
|
+
return buildHeapSample(readMemory(), limit);
|
|
107396
|
+
};
|
|
107397
|
+
const maybeReclaim = (sample) => {
|
|
107398
|
+
if (reclaimOptions === void 0) return;
|
|
107399
|
+
if (!shouldReclaim(sample, triggerMb)) return;
|
|
107400
|
+
if (now() - lastReclaimAt < minIntervalMs) return;
|
|
107401
|
+
lastReclaimAt = now();
|
|
107402
|
+
const startedAt = now();
|
|
107403
|
+
try {
|
|
107404
|
+
reclaimOptions.reclaim();
|
|
107405
|
+
} catch (error) {
|
|
107406
|
+
sink.warn(`[mem] reclaim failed \u2014 ${error instanceof Error ? error.message : String(error)}`);
|
|
107407
|
+
return;
|
|
107408
|
+
}
|
|
107409
|
+
const after = read();
|
|
107410
|
+
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`);
|
|
107411
|
+
};
|
|
107412
|
+
let mode = "steady";
|
|
107413
|
+
let lastLoggedAt = Number.NEGATIVE_INFINITY;
|
|
107414
|
+
const probeIntervalMs = Math.min(fastIntervalMs, intervalMs);
|
|
107415
|
+
const tick = () => {
|
|
107416
|
+
try {
|
|
107417
|
+
const sample = read();
|
|
107418
|
+
const previous = mode;
|
|
107419
|
+
mode = nextMode(previous, sample.usedRatio, escalateRatio, deescalateRatio);
|
|
107420
|
+
const at = now();
|
|
107421
|
+
const due = at - lastLoggedAt >= intervalMs;
|
|
107422
|
+
if (mode === "escalated" || due) {
|
|
107423
|
+
lastLoggedAt = at;
|
|
107424
|
+
const line = format2(label, sample);
|
|
107425
|
+
if (sample.nearLimit) sink.warn(`${line} \u2014 APPROACHING HEAP LIMIT`);
|
|
107426
|
+
else if (mode === "escalated") sink.warn(`${line} \u2014 heap elevated, sampling every ${probeIntervalMs}ms`);
|
|
107427
|
+
else sink.info(line);
|
|
107428
|
+
}
|
|
107429
|
+
if (previous === "escalated" && mode === "steady") sink.info(`[mem] ${label} heap back to routine \u2014 logging every ${intervalMs}ms`);
|
|
107430
|
+
maybeReclaim(sample);
|
|
107431
|
+
} catch {
|
|
107432
|
+
}
|
|
107433
|
+
};
|
|
107434
|
+
const timer = setInterval(tick, probeIntervalMs);
|
|
107435
|
+
timer.unref?.();
|
|
107436
|
+
tick();
|
|
107437
|
+
let stopped = false;
|
|
107438
|
+
return () => {
|
|
107439
|
+
if (stopped) return;
|
|
107440
|
+
stopped = true;
|
|
107441
|
+
clearInterval(timer);
|
|
107442
|
+
};
|
|
107443
|
+
}
|
|
107444
|
+
var RUNNER_HEAP_WATCH_INTERVAL_MS = 3e5;
|
|
107445
|
+
function startRunnerHeapWatch(options) {
|
|
107446
|
+
if (options.heapProfile !== "heavy") return void 0;
|
|
107447
|
+
const intervalMs = options.intervalMs ?? 3e5;
|
|
107448
|
+
if (options.reclaimSwitch === "off") return startHeapWatch(options.label, options.sink, intervalMs);
|
|
107449
|
+
let reclaimOptions = options.reclaimOptions;
|
|
107450
|
+
if (reclaimOptions === void 0) {
|
|
107451
|
+
const reclaimer = createV8Reclaimer();
|
|
107452
|
+
reclaimOptions = reclaimer === void 0 ? void 0 : { reclaim: reclaimer };
|
|
107453
|
+
}
|
|
107454
|
+
return startHeapWatch(options.label, options.sink, intervalMs, reclaimOptions);
|
|
107455
|
+
}
|
|
107165
107456
|
function trimSlashes(s) {
|
|
107166
107457
|
return s.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
107167
107458
|
}
|
|
@@ -113520,6 +113811,30 @@ var require_manifest_python_deps_BtVjvfvj = __commonJS({
|
|
|
113520
113811
|
return FrameDecoder;
|
|
113521
113812
|
}
|
|
113522
113813
|
});
|
|
113814
|
+
Object.defineProperty(exports, "HEAP_RECLAIM_MIN_INTERVAL_MS", {
|
|
113815
|
+
enumerable: true,
|
|
113816
|
+
get: function() {
|
|
113817
|
+
return HEAP_RECLAIM_MIN_INTERVAL_MS;
|
|
113818
|
+
}
|
|
113819
|
+
});
|
|
113820
|
+
Object.defineProperty(exports, "HEAP_RECLAIM_TRIGGER_MB", {
|
|
113821
|
+
enumerable: true,
|
|
113822
|
+
get: function() {
|
|
113823
|
+
return HEAP_RECLAIM_TRIGGER_MB;
|
|
113824
|
+
}
|
|
113825
|
+
});
|
|
113826
|
+
Object.defineProperty(exports, "HEAP_WATCH_INTERVAL_MS", {
|
|
113827
|
+
enumerable: true,
|
|
113828
|
+
get: function() {
|
|
113829
|
+
return HEAP_WATCH_INTERVAL_MS;
|
|
113830
|
+
}
|
|
113831
|
+
});
|
|
113832
|
+
Object.defineProperty(exports, "HEAP_WATCH_WARN_RATIO", {
|
|
113833
|
+
enumerable: true,
|
|
113834
|
+
get: function() {
|
|
113835
|
+
return HEAP_WATCH_WARN_RATIO;
|
|
113836
|
+
}
|
|
113837
|
+
});
|
|
113523
113838
|
Object.defineProperty(exports, "HUB_CAP_FWD_ACTION", {
|
|
113524
113839
|
enumerable: true,
|
|
113525
113840
|
get: function() {
|
|
@@ -113550,6 +113865,12 @@ var require_manifest_python_deps_BtVjvfvj = __commonJS({
|
|
|
113550
113865
|
return NATIVE_PROVIDER_SERVICE_INFIX;
|
|
113551
113866
|
}
|
|
113552
113867
|
});
|
|
113868
|
+
Object.defineProperty(exports, "RUNNER_HEAP_WATCH_INTERVAL_MS", {
|
|
113869
|
+
enumerable: true,
|
|
113870
|
+
get: function() {
|
|
113871
|
+
return RUNNER_HEAP_WATCH_INTERVAL_MS;
|
|
113872
|
+
}
|
|
113873
|
+
});
|
|
113553
113874
|
Object.defineProperty(exports, "SocketChannel", {
|
|
113554
113875
|
enumerable: true,
|
|
113555
113876
|
get: function() {
|
|
@@ -113598,6 +113919,12 @@ var require_manifest_python_deps_BtVjvfvj = __commonJS({
|
|
|
113598
113919
|
return brokerTransportLink;
|
|
113599
113920
|
}
|
|
113600
113921
|
});
|
|
113922
|
+
Object.defineProperty(exports, "buildHeapSample", {
|
|
113923
|
+
enumerable: true,
|
|
113924
|
+
get: function() {
|
|
113925
|
+
return buildHeapSample;
|
|
113926
|
+
}
|
|
113927
|
+
});
|
|
113601
113928
|
Object.defineProperty(exports, "buildLinkChain", {
|
|
113602
113929
|
enumerable: true,
|
|
113603
113930
|
get: function() {
|
|
@@ -113742,6 +114069,12 @@ var require_manifest_python_deps_BtVjvfvj = __commonJS({
|
|
|
113742
114069
|
return createUdsLoggerWithControl;
|
|
113743
114070
|
}
|
|
113744
114071
|
});
|
|
114072
|
+
Object.defineProperty(exports, "createV8Reclaimer", {
|
|
114073
|
+
enumerable: true,
|
|
114074
|
+
get: function() {
|
|
114075
|
+
return createV8Reclaimer;
|
|
114076
|
+
}
|
|
114077
|
+
});
|
|
113745
114078
|
Object.defineProperty(exports, "deserializeTypedArrays", {
|
|
113746
114079
|
enumerable: true,
|
|
113747
114080
|
get: function() {
|
|
@@ -113904,6 +114237,30 @@ var require_manifest_python_deps_BtVjvfvj = __commonJS({
|
|
|
113904
114237
|
return setWorkerNativeCapsChangeListener;
|
|
113905
114238
|
}
|
|
113906
114239
|
});
|
|
114240
|
+
Object.defineProperty(exports, "shouldReclaim", {
|
|
114241
|
+
enumerable: true,
|
|
114242
|
+
get: function() {
|
|
114243
|
+
return shouldReclaim;
|
|
114244
|
+
}
|
|
114245
|
+
});
|
|
114246
|
+
Object.defineProperty(exports, "startHeapWatch", {
|
|
114247
|
+
enumerable: true,
|
|
114248
|
+
get: function() {
|
|
114249
|
+
return startHeapWatch;
|
|
114250
|
+
}
|
|
114251
|
+
});
|
|
114252
|
+
Object.defineProperty(exports, "startRunnerHeapWatch", {
|
|
114253
|
+
enumerable: true,
|
|
114254
|
+
get: function() {
|
|
114255
|
+
return startRunnerHeapWatch;
|
|
114256
|
+
}
|
|
114257
|
+
});
|
|
114258
|
+
Object.defineProperty(exports, "strandedMb", {
|
|
114259
|
+
enumerable: true,
|
|
114260
|
+
get: function() {
|
|
114261
|
+
return strandedMb;
|
|
114262
|
+
}
|
|
114263
|
+
});
|
|
113907
114264
|
Object.defineProperty(exports, "subscribePassthrough", {
|
|
113908
114265
|
enumerable: true,
|
|
113909
114266
|
get: function() {
|
|
@@ -117724,7 +118081,7 @@ var require_dist3 = __commonJS({
|
|
|
117724
118081
|
"use strict";
|
|
117725
118082
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
117726
118083
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
117727
|
-
var require_dist10 =
|
|
118084
|
+
var require_dist10 = require_dist_DR1VmGz6();
|
|
117728
118085
|
var require_builtins_alerts_alerts_addon = require_alerts_addon();
|
|
117729
118086
|
require_alerts();
|
|
117730
118087
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
@@ -117749,7 +118106,7 @@ var require_dist3 = __commonJS({
|
|
|
117749
118106
|
require_system_config();
|
|
117750
118107
|
var require_builtins_winston_logging_index = require_winston_logging();
|
|
117751
118108
|
var require_file_data_plane = require_file_data_plane_DUHPHa_Y();
|
|
117752
|
-
var require_manifest_python_deps =
|
|
118109
|
+
var require_manifest_python_deps = require_manifest_python_deps_GejnH_L();
|
|
117753
118110
|
var require_resource_monitor = require_resource_monitor_CdnzxBLP();
|
|
117754
118111
|
var require_custom_action_registry = require_custom_action_registry_jY0NOZK8();
|
|
117755
118112
|
var zod = require_zod();
|
|
@@ -117767,8 +118124,6 @@ var require_dist3 = __commonJS({
|
|
|
117767
118124
|
var node_fs$1 = require_chunk.__toESM(node_fs, 1);
|
|
117768
118125
|
node_fs = require_chunk.__toESM(node_fs);
|
|
117769
118126
|
var node_http = __require("http");
|
|
117770
|
-
var node_v8 = __require("v8");
|
|
117771
|
-
node_v8 = require_chunk.__toESM(node_v8);
|
|
117772
118127
|
var node_vm = __require("vm");
|
|
117773
118128
|
node_vm = require_chunk.__toESM(node_vm);
|
|
117774
118129
|
var _camstack_types_addon = require_addon();
|
|
@@ -118102,120 +118457,6 @@ var require_dist3 = __commonJS({
|
|
|
118102
118457
|
if (replayBody !== void 0) upstream.end(replayBody);
|
|
118103
118458
|
else clientReq.pipe(upstream);
|
|
118104
118459
|
}
|
|
118105
|
-
var HEAP_WATCH_INTERVAL_MS = 6e4;
|
|
118106
|
-
var HEAP_WATCH_WARN_RATIO = 0.8;
|
|
118107
|
-
var HEAP_WATCH_ESCALATE_RATIO = 0.7;
|
|
118108
|
-
var HEAP_WATCH_DEESCALATE_RATIO = 0.6;
|
|
118109
|
-
function nextMode(current, usedRatio, escalateRatio = HEAP_WATCH_ESCALATE_RATIO, deescalateRatio = HEAP_WATCH_DEESCALATE_RATIO) {
|
|
118110
|
-
if (current === "escalated") return usedRatio < deescalateRatio ? "steady" : "escalated";
|
|
118111
|
-
return usedRatio >= escalateRatio ? "escalated" : "steady";
|
|
118112
|
-
}
|
|
118113
|
-
var HEAP_RECLAIM_TRIGGER_MB = 1536;
|
|
118114
|
-
var HEAP_RECLAIM_MIN_INTERVAL_MS = 6e5;
|
|
118115
|
-
var MB = (bytes) => Math.round(bytes / 1048576);
|
|
118116
|
-
function buildHeapSample(mem, heapLimitBytes, warnRatio = HEAP_WATCH_WARN_RATIO) {
|
|
118117
|
-
const usedRatio = heapLimitBytes > 0 ? mem.heapUsed / heapLimitBytes : 0;
|
|
118118
|
-
return {
|
|
118119
|
-
rssMb: MB(mem.rss),
|
|
118120
|
-
heapUsedMb: MB(mem.heapUsed),
|
|
118121
|
-
heapTotalMb: MB(mem.heapTotal),
|
|
118122
|
-
heapLimitMb: MB(heapLimitBytes),
|
|
118123
|
-
externalMb: MB(mem.external),
|
|
118124
|
-
arrayBuffersMb: MB(mem.arrayBuffers),
|
|
118125
|
-
usedRatio: Math.round(usedRatio * 100) / 100,
|
|
118126
|
-
nearLimit: usedRatio >= warnRatio
|
|
118127
|
-
};
|
|
118128
|
-
}
|
|
118129
|
-
var consoleSink = {
|
|
118130
|
-
info: (line) => console.log(line),
|
|
118131
|
-
warn: (line) => console.warn(line)
|
|
118132
|
-
};
|
|
118133
|
-
function strandedMb(s) {
|
|
118134
|
-
return s.rssMb - s.heapUsedMb - s.externalMb;
|
|
118135
|
-
}
|
|
118136
|
-
function shouldReclaim(s, triggerMb = HEAP_RECLAIM_TRIGGER_MB) {
|
|
118137
|
-
return strandedMb(s) > triggerMb;
|
|
118138
|
-
}
|
|
118139
|
-
function isGcFunction(value) {
|
|
118140
|
-
return typeof value === "function";
|
|
118141
|
-
}
|
|
118142
|
-
function createV8Reclaimer() {
|
|
118143
|
-
try {
|
|
118144
|
-
node_v8.setFlagsFromString("--expose-gc");
|
|
118145
|
-
const gc = node_vm.runInNewContext("gc");
|
|
118146
|
-
node_v8.setFlagsFromString("--no-expose-gc");
|
|
118147
|
-
if (!isGcFunction(gc)) return void 0;
|
|
118148
|
-
return () => gc({
|
|
118149
|
-
execution: "sync",
|
|
118150
|
-
flavor: "last-resort"
|
|
118151
|
-
});
|
|
118152
|
-
} catch {
|
|
118153
|
-
return;
|
|
118154
|
-
}
|
|
118155
|
-
}
|
|
118156
|
-
function format$1(label, s) {
|
|
118157
|
-
return `[mem] ${label} rss=${s.rssMb}MB heapUsed=${s.heapUsedMb}MB heapTotal=${s.heapTotalMb}MB heapLimit=${s.heapLimitMb}MB used=${Math.round(s.usedRatio * 100)}% external=${s.externalMb}MB arrayBuffers=${s.arrayBuffersMb}MB`;
|
|
118158
|
-
}
|
|
118159
|
-
function startHeapWatch(label = "hub-main", sink = consoleSink, intervalMs = HEAP_WATCH_INTERVAL_MS, reclaimOptions) {
|
|
118160
|
-
const readMemory = reclaimOptions?.readMemory ?? (() => process.memoryUsage());
|
|
118161
|
-
const now = reclaimOptions?.now ?? (() => Date.now());
|
|
118162
|
-
const triggerMb = reclaimOptions?.triggerMb ?? 1536;
|
|
118163
|
-
const minIntervalMs = reclaimOptions?.minIntervalMs ?? 6e5;
|
|
118164
|
-
const fastIntervalMs = reclaimOptions?.fastIntervalMs ?? 2e3;
|
|
118165
|
-
const escalateRatio = reclaimOptions?.escalateRatio ?? 0.7;
|
|
118166
|
-
const deescalateRatio = reclaimOptions?.deescalateRatio ?? 0.6;
|
|
118167
|
-
let lastReclaimAt = Number.NEGATIVE_INFINITY;
|
|
118168
|
-
const read = () => {
|
|
118169
|
-
const limit = reclaimOptions?.heapLimitBytes ?? node_v8.getHeapStatistics().heap_size_limit;
|
|
118170
|
-
return buildHeapSample(readMemory(), limit);
|
|
118171
|
-
};
|
|
118172
|
-
const maybeReclaim = (sample) => {
|
|
118173
|
-
if (reclaimOptions === void 0) return;
|
|
118174
|
-
if (!shouldReclaim(sample, triggerMb)) return;
|
|
118175
|
-
if (now() - lastReclaimAt < minIntervalMs) return;
|
|
118176
|
-
lastReclaimAt = now();
|
|
118177
|
-
const startedAt = now();
|
|
118178
|
-
try {
|
|
118179
|
-
reclaimOptions.reclaim();
|
|
118180
|
-
} catch (error) {
|
|
118181
|
-
sink.warn(`[mem] reclaim failed \u2014 ${error instanceof Error ? error.message : String(error)}`);
|
|
118182
|
-
return;
|
|
118183
|
-
}
|
|
118184
|
-
const after = read();
|
|
118185
|
-
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`);
|
|
118186
|
-
};
|
|
118187
|
-
let mode = "steady";
|
|
118188
|
-
let lastLoggedAt = Number.NEGATIVE_INFINITY;
|
|
118189
|
-
const probeIntervalMs = Math.min(fastIntervalMs, intervalMs);
|
|
118190
|
-
const tick = () => {
|
|
118191
|
-
try {
|
|
118192
|
-
const sample = read();
|
|
118193
|
-
const previous = mode;
|
|
118194
|
-
mode = nextMode(previous, sample.usedRatio, escalateRatio, deescalateRatio);
|
|
118195
|
-
const at2 = now();
|
|
118196
|
-
const due = at2 - lastLoggedAt >= intervalMs;
|
|
118197
|
-
if (mode === "escalated" || due) {
|
|
118198
|
-
lastLoggedAt = at2;
|
|
118199
|
-
const line = format$1(label, sample);
|
|
118200
|
-
if (sample.nearLimit) sink.warn(`${line} \u2014 APPROACHING HEAP LIMIT`);
|
|
118201
|
-
else if (mode === "escalated") sink.warn(`${line} \u2014 heap elevated, sampling every ${probeIntervalMs}ms`);
|
|
118202
|
-
else sink.info(line);
|
|
118203
|
-
}
|
|
118204
|
-
if (previous === "escalated" && mode === "steady") sink.info(`[mem] ${label} heap back to routine \u2014 logging every ${intervalMs}ms`);
|
|
118205
|
-
maybeReclaim(sample);
|
|
118206
|
-
} catch {
|
|
118207
|
-
}
|
|
118208
|
-
};
|
|
118209
|
-
const timer = setInterval(tick, probeIntervalMs);
|
|
118210
|
-
timer.unref?.();
|
|
118211
|
-
tick();
|
|
118212
|
-
let stopped = false;
|
|
118213
|
-
return () => {
|
|
118214
|
-
if (stopped) return;
|
|
118215
|
-
stopped = true;
|
|
118216
|
-
clearInterval(timer);
|
|
118217
|
-
};
|
|
118218
|
-
}
|
|
118219
118460
|
var VALID_TRANSITIONS = {
|
|
118220
118461
|
stopped: ["starting", "disabled"],
|
|
118221
118462
|
starting: [
|
|
@@ -118447,17 +118688,27 @@ var require_dist3 = __commonJS({
|
|
|
118447
118688
|
return bare;
|
|
118448
118689
|
}
|
|
118449
118690
|
var PRUNE_TARGET_RATIO = 0.9;
|
|
118691
|
+
var DEFAULT_MAX_TOTAL_LOG_ENTRIES = 5e4;
|
|
118450
118692
|
var PartitionedLogBuffer = class {
|
|
118451
118693
|
buffers = /* @__PURE__ */ new Map();
|
|
118694
|
+
/**
|
|
118695
|
+
* Bucket key → the write ordinal of its last `push`. A monotonic counter, not
|
|
118696
|
+
* a clock: eviction order must follow the order writes actually happened, and
|
|
118697
|
+
* a wall clock that steps backwards would pick the wrong victim.
|
|
118698
|
+
*/
|
|
118699
|
+
lastWriteSeq = /* @__PURE__ */ new Map();
|
|
118700
|
+
writeSeq = 0;
|
|
118452
118701
|
perAddonCapacity;
|
|
118453
118702
|
maxTotalEntries;
|
|
118454
118703
|
pruneLevel;
|
|
118704
|
+
maxBuckets;
|
|
118455
118705
|
/** Running total, so `push` does not walk every bucket to decide. */
|
|
118456
118706
|
totalEntries = 0;
|
|
118457
118707
|
constructor(perAddonCapacity = 5e3, options = {}) {
|
|
118458
118708
|
this.perAddonCapacity = options.perAddonCapacity ?? perAddonCapacity;
|
|
118459
|
-
this.maxTotalEntries = options.maxTotalEntries
|
|
118709
|
+
this.maxTotalEntries = options.maxTotalEntries === void 0 ? DEFAULT_MAX_TOTAL_LOG_ENTRIES : options.maxTotalEntries;
|
|
118460
118710
|
this.pruneLevel = options.pruneLevel ?? "debug";
|
|
118711
|
+
this.maxBuckets = options.maxBuckets === void 0 ? 128 : options.maxBuckets;
|
|
118461
118712
|
}
|
|
118462
118713
|
/**
|
|
118463
118714
|
* Apply new limits at runtime. Shrinking takes effect as entries are pushed
|
|
@@ -118470,6 +118721,7 @@ var require_dist3 = __commonJS({
|
|
|
118470
118721
|
if (options.perAddonCapacity !== void 0 && options.perAddonCapacity > 0) this.perAddonCapacity = options.perAddonCapacity;
|
|
118471
118722
|
if (options.maxTotalEntries !== void 0) this.maxTotalEntries = options.maxTotalEntries;
|
|
118472
118723
|
if (options.pruneLevel !== void 0) this.pruneLevel = options.pruneLevel;
|
|
118724
|
+
if (options.maxBuckets !== void 0) this.maxBuckets = options.maxBuckets;
|
|
118473
118725
|
}
|
|
118474
118726
|
bufferFor(key) {
|
|
118475
118727
|
let buf = this.buffers.get(key);
|
|
@@ -118480,34 +118732,82 @@ var require_dist3 = __commonJS({
|
|
|
118480
118732
|
return buf;
|
|
118481
118733
|
}
|
|
118482
118734
|
push(entry) {
|
|
118483
|
-
const
|
|
118735
|
+
const key = addonBucketKey(entry.tags?.addonId);
|
|
118736
|
+
const buf = this.bufferFor(key);
|
|
118484
118737
|
const before = buf.size();
|
|
118485
118738
|
buf.push(entry);
|
|
118486
118739
|
this.totalEntries += buf.size() - before;
|
|
118487
|
-
|
|
118740
|
+
this.lastWriteSeq.set(key, ++this.writeSeq);
|
|
118741
|
+
if (this.maxBuckets !== null && this.buffers.size > this.maxBuckets) this.evictBuckets(this.buffers.size - this.maxBuckets, key);
|
|
118742
|
+
if (this.maxTotalEntries !== null && this.totalEntries > this.maxTotalEntries) this.enforceTotalCap(this.maxTotalEntries, key);
|
|
118488
118743
|
}
|
|
118489
118744
|
/**
|
|
118490
|
-
* Bring the total down to `PRUNE_TARGET_RATIO × cap
|
|
118491
|
-
*
|
|
118492
|
-
*
|
|
118493
|
-
*
|
|
118494
|
-
|
|
118495
|
-
|
|
118745
|
+
* Bring the total down to `PRUNE_TARGET_RATIO × cap`.
|
|
118746
|
+
*
|
|
118747
|
+
* Two passes, in this order and no other: first discard prunable entries,
|
|
118748
|
+
* largest bucket first, which is free of information cost. Only if that
|
|
118749
|
+
* cannot reach the target does the second pass drop WHOLE buckets,
|
|
118750
|
+
* least-recently-written first — the bound has to hold even when every
|
|
118751
|
+
* retained entry is above `pruneLevel`, and a bucket nobody has written to is
|
|
118752
|
+
* the cheapest thing in the structure to lose.
|
|
118753
|
+
*/
|
|
118754
|
+
enforceTotalCap(cap, protectedKey) {
|
|
118496
118755
|
const target = Math.max(1, Math.floor(cap * PRUNE_TARGET_RATIO));
|
|
118497
118756
|
let deficit = this.totalEntries - target;
|
|
118498
118757
|
if (deficit <= 0) return;
|
|
118499
|
-
const bySizeDesc = [...this.buffers.
|
|
118500
|
-
for (const buf of bySizeDesc) {
|
|
118758
|
+
const bySizeDesc = [...this.buffers.entries()].toSorted((a, b) => b[1].size() - a[1].size());
|
|
118759
|
+
for (const [key, buf] of bySizeDesc) {
|
|
118501
118760
|
if (deficit <= 0) break;
|
|
118502
118761
|
const removed = buf.pruneOldestAtOrBelow(this.pruneLevel, deficit);
|
|
118503
118762
|
this.totalEntries -= removed;
|
|
118504
118763
|
deficit -= removed;
|
|
118764
|
+
if (buf.size() === 0) this.dropBucket(key);
|
|
118765
|
+
}
|
|
118766
|
+
if (deficit > 0) this.evictBucketsUntil(target, protectedKey);
|
|
118767
|
+
}
|
|
118768
|
+
/**
|
|
118769
|
+
* Drop whole buckets, least-recently-WRITTEN first, until the total is at or
|
|
118770
|
+
* below `target`. Never touches `protectedKey` (the bucket being written to
|
|
118771
|
+
* right now) and always leaves at least one bucket standing.
|
|
118772
|
+
*/
|
|
118773
|
+
evictBucketsUntil(target, protectedKey) {
|
|
118774
|
+
for (const key of this.evictionOrder(protectedKey)) {
|
|
118775
|
+
if (this.totalEntries <= target || this.buffers.size <= 1) return;
|
|
118776
|
+
this.dropBucket(key);
|
|
118505
118777
|
}
|
|
118506
118778
|
}
|
|
118779
|
+
/** Drop exactly `count` buckets, least-recently-written first. */
|
|
118780
|
+
evictBuckets(count, protectedKey) {
|
|
118781
|
+
let remaining = count;
|
|
118782
|
+
for (const key of this.evictionOrder(protectedKey)) {
|
|
118783
|
+
if (remaining <= 0 || this.buffers.size <= 1) return;
|
|
118784
|
+
this.dropBucket(key);
|
|
118785
|
+
remaining -= 1;
|
|
118786
|
+
}
|
|
118787
|
+
}
|
|
118788
|
+
/** Bucket keys ordered least-recently-written first, excluding `protectedKey`.
|
|
118789
|
+
* A bucket with no recorded write sorts first — it can only be a leftover. */
|
|
118790
|
+
evictionOrder(protectedKey) {
|
|
118791
|
+
return [...this.buffers.keys()].filter((key) => key !== protectedKey).toSorted((a, b) => (this.lastWriteSeq.get(a) ?? 0) - (this.lastWriteSeq.get(b) ?? 0));
|
|
118792
|
+
}
|
|
118793
|
+
/** Remove a bucket and everything that indexes it. An empty ring left behind
|
|
118794
|
+
* is the leak this class shipped with — the map only ever grew. */
|
|
118795
|
+
dropBucket(key) {
|
|
118796
|
+
const buf = this.buffers.get(key);
|
|
118797
|
+
if (buf === void 0) return;
|
|
118798
|
+
this.totalEntries -= buf.size();
|
|
118799
|
+
this.buffers.delete(key);
|
|
118800
|
+
this.lastWriteSeq.delete(key);
|
|
118801
|
+
}
|
|
118507
118802
|
/** Entries retained across every bucket. */
|
|
118508
118803
|
size() {
|
|
118509
118804
|
return this.totalEntries;
|
|
118510
118805
|
}
|
|
118806
|
+
/** Buckets currently retained. Observability for the aggregate bound — an
|
|
118807
|
+
* addon roster that only ever grows is what this class had to stop doing. */
|
|
118808
|
+
bucketCount() {
|
|
118809
|
+
return this.buffers.size;
|
|
118810
|
+
}
|
|
118511
118811
|
/** Every retained entry across all buckets, newest-first (mirrors
|
|
118512
118812
|
* {@link LogRingBuffer.getAll}). Used to replay history to a destination. */
|
|
118513
118813
|
getAll() {
|
|
@@ -118535,13 +118835,25 @@ var require_dist3 = __commonJS({
|
|
|
118535
118835
|
if (pinned !== null) {
|
|
118536
118836
|
const removed2 = this.buffers.get(pinned)?.clear(filter) ?? 0;
|
|
118537
118837
|
this.totalEntries -= removed2;
|
|
118838
|
+
this.dropIfEmpty(pinned);
|
|
118538
118839
|
return removed2;
|
|
118539
118840
|
}
|
|
118540
118841
|
let removed = 0;
|
|
118541
|
-
for (const buf of this.buffers.
|
|
118842
|
+
for (const [key, buf] of [...this.buffers.entries()]) {
|
|
118843
|
+
removed += buf.clear(filter);
|
|
118844
|
+
this.dropIfEmpty(key);
|
|
118845
|
+
}
|
|
118542
118846
|
this.totalEntries -= removed;
|
|
118543
118847
|
return removed;
|
|
118544
118848
|
}
|
|
118849
|
+
/** An emptied bucket is removed rather than left as an empty ring: the map
|
|
118850
|
+
* used to grow with every addon id that ever logged and never shrink. */
|
|
118851
|
+
dropIfEmpty(key) {
|
|
118852
|
+
if (this.buffers.get(key)?.size() === 0) {
|
|
118853
|
+
this.buffers.delete(key);
|
|
118854
|
+
this.lastWriteSeq.delete(key);
|
|
118855
|
+
}
|
|
118856
|
+
}
|
|
118545
118857
|
/** The bucket key a filter pins to via `tags.addonId`, or null when the
|
|
118546
118858
|
* filter doesn't constrain the addon (→ scan all buckets). */
|
|
118547
118859
|
pinnedAddonKey(filter) {
|
|
@@ -118637,11 +118949,13 @@ var require_dist3 = __commonJS({
|
|
|
118637
118949
|
* `perAddonCapacity` bounds EACH addon's bucket, not the total — a chatty addon
|
|
118638
118950
|
* evicts only its own lines, so quiet addons keep their sparse history.
|
|
118639
118951
|
*
|
|
118640
|
-
* `options.maxTotalEntries`
|
|
118641
|
-
* per-bucket bound scales with addon count
|
|
118642
|
-
*
|
|
118643
|
-
* `
|
|
118644
|
-
*
|
|
118952
|
+
* `options.maxTotalEntries` ceilings the total across all buckets, because the
|
|
118953
|
+
* per-bucket bound scales with addon count, and `options.maxBuckets` ceilings the
|
|
118954
|
+
* bucket count itself. When the total is exceeded, entries at or below
|
|
118955
|
+
* `options.pruneLevel` (default `debug`) are discarded first — a live addon's
|
|
118956
|
+
* `warn`/`error` are never evicted to satisfy it — and only if that cannot reach
|
|
118957
|
+
* the target are whole least-recently-written buckets dropped. Both bounds are ON
|
|
118958
|
+
* by default; pass an explicit `null` to opt out. See {@link PartitionedLogBuffer}.
|
|
118645
118959
|
*/
|
|
118646
118960
|
constructor(perAddonCapacity = 5e3, options = {}) {
|
|
118647
118961
|
this.ringBuffer = new PartitionedLogBuffer(perAddonCapacity, options);
|
|
@@ -197242,22 +197556,33 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
197242
197556
|
});
|
|
197243
197557
|
});
|
|
197244
197558
|
}
|
|
197559
|
+
var EMPTY_HEAP_DECLARATION = {
|
|
197560
|
+
profile: void 0,
|
|
197561
|
+
maxOldSpaceMb: void 0
|
|
197562
|
+
};
|
|
197245
197563
|
var heapProfileCache = /* @__PURE__ */ new Map();
|
|
197246
|
-
function
|
|
197564
|
+
function readAddonHeapDeclaration(spec) {
|
|
197247
197565
|
const cacheKey = `${spec.addonDir}::${spec.addonId}`;
|
|
197248
197566
|
const cached = heapProfileCache.get(cacheKey);
|
|
197249
|
-
if (cached !== void 0
|
|
197250
|
-
let
|
|
197567
|
+
if (cached !== void 0) return cached;
|
|
197568
|
+
let declaration = EMPTY_HEAP_DECLARATION;
|
|
197251
197569
|
for (const manifestPath of [node_path.join(spec.addonDir, "package.json"), node_path.join(node_path.dirname(spec.addonDir), "package.json")]) try {
|
|
197252
197570
|
const raw = node_fs.readFileSync(manifestPath, "utf8");
|
|
197253
197571
|
const parsed = JSON.parse(raw);
|
|
197254
|
-
profile = extractHeapProfile(parsed, spec.addonId);
|
|
197255
|
-
|
|
197572
|
+
const profile = extractHeapProfile(parsed, spec.addonId);
|
|
197573
|
+
const maxOldSpaceMb = extractMaxOldSpaceMb(parsed, spec.addonId);
|
|
197574
|
+
if (profile !== void 0 || maxOldSpaceMb !== void 0) {
|
|
197575
|
+
declaration = {
|
|
197576
|
+
profile,
|
|
197577
|
+
maxOldSpaceMb
|
|
197578
|
+
};
|
|
197579
|
+
break;
|
|
197580
|
+
}
|
|
197256
197581
|
if (manifestHasAddon(parsed, spec.addonId)) break;
|
|
197257
197582
|
} catch {
|
|
197258
197583
|
}
|
|
197259
|
-
heapProfileCache.set(cacheKey,
|
|
197260
|
-
return
|
|
197584
|
+
heapProfileCache.set(cacheKey, declaration);
|
|
197585
|
+
return declaration;
|
|
197261
197586
|
}
|
|
197262
197587
|
function invalidateHeapProfiles(addons) {
|
|
197263
197588
|
for (const spec of addons) heapProfileCache.delete(`${spec.addonDir}::${spec.addonId}`);
|
|
@@ -197269,6 +197594,10 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
197269
197594
|
const value = readManifestAddons(parsed).find((a) => a.id === addonId)?.execution?.heapProfile;
|
|
197270
197595
|
return value === "heavy" || value === "light" ? value : void 0;
|
|
197271
197596
|
}
|
|
197597
|
+
function extractMaxOldSpaceMb(parsed, addonId) {
|
|
197598
|
+
const value = readManifestAddons(parsed).find((a) => a.id === addonId)?.execution?.maxOldSpaceMb;
|
|
197599
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : void 0;
|
|
197600
|
+
}
|
|
197272
197601
|
function readManifestAddons(parsed) {
|
|
197273
197602
|
if (typeof parsed !== "object" || parsed === null) return [];
|
|
197274
197603
|
const camstack = parsed.camstack;
|
|
@@ -197277,15 +197606,25 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
197277
197606
|
return Array.isArray(addons) ? addons : [];
|
|
197278
197607
|
}
|
|
197279
197608
|
function isHeavyRunner(addons) {
|
|
197280
|
-
return addons.some((a) =>
|
|
197609
|
+
return addons.some((a) => readAddonHeapDeclaration(a).profile === "heavy");
|
|
197610
|
+
}
|
|
197611
|
+
var HEAVY_MAX_OLD_MB_DEFAULT = 1024;
|
|
197612
|
+
function runnerMaxOldSpaceMb(addons, heavy) {
|
|
197613
|
+
const envDefault = heavy ? positiveIntEnv("CAMSTACK_RUNNER_HEAVY_MAX_OLD_MB", HEAVY_MAX_OLD_MB_DEFAULT) : positiveIntEnv("CAMSTACK_RUNNER_LIGHT_MAX_OLD_MB", 0);
|
|
197614
|
+
const declared = addons.map((a) => readAddonHeapDeclaration(a).maxOldSpaceMb).filter((mb) => mb !== void 0);
|
|
197615
|
+
if (declared.length === 0) return envDefault;
|
|
197616
|
+
if (declared.includes(0)) return 0;
|
|
197617
|
+
return Math.max(...declared);
|
|
197281
197618
|
}
|
|
197282
197619
|
function runnerHeapFlags(addons) {
|
|
197283
197620
|
if (process.env["CAMSTACK_RUNNER_HEAP_TUNING"] === "off") return [];
|
|
197284
|
-
|
|
197285
|
-
const semiMb = positiveIntEnv("CAMSTACK_RUNNER_LIGHT_SEMI_SPACE_MB", 2);
|
|
197286
|
-
const oldMb = positiveIntEnv("CAMSTACK_RUNNER_LIGHT_MAX_OLD_MB", 0);
|
|
197621
|
+
const heavy = isHeavyRunner(addons);
|
|
197287
197622
|
const flags = [];
|
|
197288
|
-
if (
|
|
197623
|
+
if (!heavy) {
|
|
197624
|
+
const semiMb = positiveIntEnv("CAMSTACK_RUNNER_LIGHT_SEMI_SPACE_MB", 2);
|
|
197625
|
+
if (semiMb > 0) flags.push(`--max-semi-space-size=${semiMb}`);
|
|
197626
|
+
}
|
|
197627
|
+
const oldMb = runnerMaxOldSpaceMb(addons, heavy);
|
|
197289
197628
|
if (oldMb > 0) flags.push(`--max-old-space-size=${oldMb}`);
|
|
197290
197629
|
return flags;
|
|
197291
197630
|
}
|
|
@@ -197332,19 +197671,21 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
197332
197671
|
function spawnRunner(runnerId, addons, env) {
|
|
197333
197672
|
const nodeId = buildNodeId(runnerId);
|
|
197334
197673
|
const runnerPath = node_path.resolve(__dirname, "addon-runner.js");
|
|
197674
|
+
const heavy = isHeavyRunner(addons);
|
|
197335
197675
|
const childEnv = {
|
|
197336
197676
|
...process.env,
|
|
197337
197677
|
CAMSTACK_RUNNER_ID: runnerId,
|
|
197338
197678
|
CAMSTACK_RUNNER_ADDONS: JSON.stringify(addons),
|
|
197339
197679
|
CAMSTACK_PARENT_NODE_ID: parentNodeId,
|
|
197340
197680
|
CAMSTACK_DATA_DIR: dataDir,
|
|
197681
|
+
CAMSTACK_RUNNER_HEAP_PROFILE: heavy ? "heavy" : "light",
|
|
197341
197682
|
CAMSTACK_LOG_LEVEL: process.env["CAMSTACK_LOG_LEVEL"] ?? "info",
|
|
197342
197683
|
...parentTcpPort !== void 0 ? { CAMSTACK_PARENT_TCP_PORT: String(parentTcpPort) } : {},
|
|
197343
197684
|
...parentUdsPath !== void 0 ? { CAMSTACK_PARENT_UDS_PATH: parentUdsPath } : {},
|
|
197344
197685
|
...env
|
|
197345
197686
|
};
|
|
197346
197687
|
const heapFlags = runnerHeapFlags(addons);
|
|
197347
|
-
capturedBroker?.logger.info(`[${runnerId}] heap profile: ${
|
|
197688
|
+
capturedBroker?.logger.info(`[${runnerId}] heap profile: ${heavy ? "heavy" : "light"} flags=[${heapFlags.join(" ")}]`);
|
|
197348
197689
|
const child = spawnFn(process.execPath, [...heapFlags, runnerPath], {
|
|
197349
197690
|
env: childEnv,
|
|
197350
197691
|
stdio: [
|
|
@@ -197388,6 +197729,15 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
197388
197729
|
capturedBroker?.logger.error(`[${runnerId}] Crash circuit-breaker tripped \u2014 ${decision.crashesInWindow} crashes in ${CRASH_WINDOW_MS}ms. Not respawning.`);
|
|
197389
197730
|
processes.set(runnerId, entry);
|
|
197390
197731
|
if (capturedBroker) forceMoleculerDisconnect(capturedBroker, entry.nodeId);
|
|
197732
|
+
emitRunnerEvent(require_dist10.EventCategory.AddonRunnerFailed, runnerId, {
|
|
197733
|
+
runnerId,
|
|
197734
|
+
nodeId: parentNodeId,
|
|
197735
|
+
addonIds: entry.runnerAddons.map((addon) => addon.addonId),
|
|
197736
|
+
crashesInWindow: decision.crashesInWindow,
|
|
197737
|
+
windowMs: CRASH_WINDOW_MS,
|
|
197738
|
+
...code !== null ? { exitCode: code } : {},
|
|
197739
|
+
...signal !== null ? { signal } : {}
|
|
197740
|
+
});
|
|
197391
197741
|
for (const addon of entry.runnerAddons) emitRunnerEvent(require_dist10.EventCategory.AddonCrashed, addon.addonId, {
|
|
197392
197742
|
addonId: addon.addonId,
|
|
197393
197743
|
error: `Crash circuit-breaker tripped after ${decision.crashesInWindow} crashes in ${CRASH_WINDOW_MS}ms`
|
|
@@ -198109,10 +198459,10 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198109
198459
|
exports.HEALTH_MONITOR_GRACE_PERIOD_MS = HEALTH_MONITOR_GRACE_PERIOD_MS;
|
|
198110
198460
|
exports.HEALTH_MONITOR_RETRY_INTERVALS_MS = HEALTH_MONITOR_RETRY_INTERVALS_MS;
|
|
198111
198461
|
exports.HEALTH_MONITOR_TICK_MS = HEALTH_MONITOR_TICK_MS;
|
|
198112
|
-
exports.HEAP_RECLAIM_MIN_INTERVAL_MS = HEAP_RECLAIM_MIN_INTERVAL_MS;
|
|
198113
|
-
exports.HEAP_RECLAIM_TRIGGER_MB = HEAP_RECLAIM_TRIGGER_MB;
|
|
198114
|
-
exports.HEAP_WATCH_INTERVAL_MS = HEAP_WATCH_INTERVAL_MS;
|
|
198115
|
-
exports.HEAP_WATCH_WARN_RATIO = HEAP_WATCH_WARN_RATIO;
|
|
198462
|
+
exports.HEAP_RECLAIM_MIN_INTERVAL_MS = require_manifest_python_deps.HEAP_RECLAIM_MIN_INTERVAL_MS;
|
|
198463
|
+
exports.HEAP_RECLAIM_TRIGGER_MB = require_manifest_python_deps.HEAP_RECLAIM_TRIGGER_MB;
|
|
198464
|
+
exports.HEAP_WATCH_INTERVAL_MS = require_manifest_python_deps.HEAP_WATCH_INTERVAL_MS;
|
|
198465
|
+
exports.HEAP_WATCH_WARN_RATIO = require_manifest_python_deps.HEAP_WATCH_WARN_RATIO;
|
|
198116
198466
|
exports.HUB_CAP_FWD_ACTION = require_manifest_python_deps.HUB_CAP_FWD_ACTION;
|
|
198117
198467
|
exports.HUB_CAP_FWD_SERVICE = require_manifest_python_deps.HUB_CAP_FWD_SERVICE;
|
|
198118
198468
|
exports.HubForwarderAddon = require_builtins_hub_forwarder_index.HubForwarderAddon$1;
|
|
@@ -198151,6 +198501,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198151
198501
|
exports.PythonEnvManager = PythonEnvManager;
|
|
198152
198502
|
exports.QUARANTINE_DIRNAME = QUARANTINE_DIRNAME;
|
|
198153
198503
|
exports.RESTART_MARKER_FILE = RESTART_MARKER_FILE;
|
|
198504
|
+
exports.RUNNER_HEAP_WATCH_INTERVAL_MS = require_manifest_python_deps.RUNNER_HEAP_WATCH_INTERVAL_MS;
|
|
198154
198505
|
exports.RUNTIME_DEFAULTS = require_dist10.RUNTIME_DEFAULTS;
|
|
198155
198506
|
exports.ReadinessRegistry = require_dist10.ReadinessRegistry;
|
|
198156
198507
|
exports.ReadinessTimeoutError = require_dist10.ReadinessTimeoutError;
|
|
@@ -198189,7 +198540,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198189
198540
|
}
|
|
198190
198541
|
});
|
|
198191
198542
|
exports.buildCapRouters = buildCapRouters;
|
|
198192
|
-
exports.buildHeapSample = buildHeapSample;
|
|
198543
|
+
exports.buildHeapSample = require_manifest_python_deps.buildHeapSample;
|
|
198193
198544
|
exports.buildLinkChain = require_manifest_python_deps.buildLinkChain;
|
|
198194
198545
|
exports.buildNativeCapProxy = require_manifest_python_deps.buildNativeCapProxy;
|
|
198195
198546
|
exports.buildNodeManifest = buildNodeManifest;
|
|
@@ -198234,7 +198585,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198234
198585
|
exports.createUdsEventBus = require_manifest_python_deps.createUdsEventBus;
|
|
198235
198586
|
exports.createUdsLogger = require_manifest_python_deps.createUdsLogger;
|
|
198236
198587
|
exports.createUdsLoggerWithControl = require_manifest_python_deps.createUdsLoggerWithControl;
|
|
198237
|
-
exports.createV8Reclaimer = createV8Reclaimer;
|
|
198588
|
+
exports.createV8Reclaimer = require_manifest_python_deps.createV8Reclaimer;
|
|
198238
198589
|
exports.deleteModelFromDisk = require_file_data_plane.deleteModelFromDisk;
|
|
198239
198590
|
exports.deriveAgentListenPort = deriveAgentListenPort;
|
|
198240
198591
|
exports.describeProviderKindDrift = describeProviderKindDrift;
|
|
@@ -198360,9 +198711,10 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198360
198711
|
exports.serializeTypedArrays = require_manifest_python_deps.serializeTypedArrays;
|
|
198361
198712
|
exports.setHubConnected = require_manifest_python_deps.setHubConnected;
|
|
198362
198713
|
exports.setNodeEventInterest = require_manifest_python_deps.setNodeEventInterest;
|
|
198363
|
-
exports.shouldReclaim = shouldReclaim;
|
|
198364
|
-
exports.startHeapWatch = startHeapWatch;
|
|
198365
|
-
exports.
|
|
198714
|
+
exports.shouldReclaim = require_manifest_python_deps.shouldReclaim;
|
|
198715
|
+
exports.startHeapWatch = require_manifest_python_deps.startHeapWatch;
|
|
198716
|
+
exports.startRunnerHeapWatch = require_manifest_python_deps.startRunnerHeapWatch;
|
|
198717
|
+
exports.strandedMb = require_manifest_python_deps.strandedMb;
|
|
198366
198718
|
exports.stripCamstackDeps = stripCamstackDeps;
|
|
198367
198719
|
exports.subscribePassthrough = require_manifest_python_deps.subscribePassthrough;
|
|
198368
198720
|
exports.udsChildLogToWorkerEntry = require_manifest_python_deps.udsChildLogToWorkerEntry;
|
|
@@ -237035,7 +237387,7 @@ var require_enums = __commonJS({
|
|
|
237035
237387
|
"../types/dist/enums.js"(exports) {
|
|
237036
237388
|
"use strict";
|
|
237037
237389
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
237038
|
-
var require_event_category =
|
|
237390
|
+
var require_event_category = require_event_category_CRPORAAz();
|
|
237039
237391
|
var EventSourceType = /* @__PURE__ */ (function(EventSourceType2) {
|
|
237040
237392
|
EventSourceType2["Addon"] = "addon";
|
|
237041
237393
|
EventSourceType2["Core"] = "core";
|
|
@@ -237055,8 +237407,8 @@ var require_dist9 = __commonJS({
|
|
|
237055
237407
|
"../types/dist/index.js"(exports) {
|
|
237056
237408
|
"use strict";
|
|
237057
237409
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
237058
|
-
var require_event_category =
|
|
237059
|
-
var require_sleep =
|
|
237410
|
+
var require_event_category = require_event_category_CRPORAAz();
|
|
237411
|
+
var require_sleep = require_sleep_CMRLJj2e();
|
|
237060
237412
|
var require_canonical_hash = require_canonical_hash_DNV8S5ET();
|
|
237061
237413
|
var require_enums2 = require_enums();
|
|
237062
237414
|
var require_err_msg = require_err_msg_COpsHMw2();
|
|
@@ -237978,6 +238330,7 @@ var require_dist9 = __commonJS({
|
|
|
237978
238330
|
preBufferSec: 10,
|
|
237979
238331
|
postBufferSec: 30
|
|
237980
238332
|
};
|
|
238333
|
+
var VISIT_MERGE_GAP_MS = DEFAULT_EVENTS_BAND_BUFFER_SEC.postBufferSec * 1e3;
|
|
237981
238334
|
var RecordingRetentionSchema = zod.z.object({
|
|
237982
238335
|
maxAgeDays: zod.z.number().min(0).optional(),
|
|
237983
238336
|
maxSizeGb: zod.z.number().min(0).optional()
|
|
@@ -238039,6 +238392,12 @@ var require_dist9 = __commonJS({
|
|
|
238039
238392
|
/** DERIVED summary of `bands`, stamped by the recorder on every save.
|
|
238040
238393
|
* Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
|
|
238041
238394
|
mode: RecordingStorageModeSchema.optional(),
|
|
238395
|
+
/**
|
|
238396
|
+
* Which assigned broker slots to record. Absent / empty = {@link
|
|
238397
|
+
* DEFAULT_RECORDING_PROFILES} (`high`+`low`) intersected with the
|
|
238398
|
+
* camera's currently assigned slots — never `mid` unless the operator
|
|
238399
|
+
* picks it, and never a slot the broker has not assigned.
|
|
238400
|
+
*/
|
|
238042
238401
|
profiles: zod.z.array(require_sleep.CamProfileSchema).optional(),
|
|
238043
238402
|
segmentSeconds: zod.z.number().int().positive().optional(),
|
|
238044
238403
|
/**
|
|
@@ -238061,6 +238420,15 @@ var require_dist9 = __commonJS({
|
|
|
238061
238420
|
if (config.bands.some((band) => band.mode === "continuous")) return "continuous";
|
|
238062
238421
|
return "events";
|
|
238063
238422
|
}
|
|
238423
|
+
var DEFAULT_RECORDING_PROFILES = ["high", "low"];
|
|
238424
|
+
function resolveRecordingProfiles(assigned, override) {
|
|
238425
|
+
if (override !== void 0 && override.length > 0) {
|
|
238426
|
+
const selected = assigned.filter((profile) => override.includes(profile));
|
|
238427
|
+
return selected.length > 0 ? selected : [...assigned];
|
|
238428
|
+
}
|
|
238429
|
+
const preferred = assigned.filter((profile) => DEFAULT_RECORDING_PROFILES.includes(profile));
|
|
238430
|
+
return preferred.length > 0 ? preferred : [...assigned];
|
|
238431
|
+
}
|
|
238064
238432
|
var RelocateJobStateSchema = zod.z.enum([
|
|
238065
238433
|
"queued",
|
|
238066
238434
|
"running",
|
|
@@ -238106,7 +238474,11 @@ var require_dist9 = __commonJS({
|
|
|
238106
238474
|
profiles: zod.z.array(zod.z.string()).optional(),
|
|
238107
238475
|
/** Copy throttle in MB/s (default 40) — the drain is a background chore,
|
|
238108
238476
|
* never allowed to starve live writers. */
|
|
238109
|
-
throttleMbps: zod.z.number().min(1).max(1e3).optional()
|
|
238477
|
+
throttleMbps: zod.z.number().min(1).max(1e3).optional(),
|
|
238478
|
+
/** Move only segments whose startMs is >= this. Absent = the whole source
|
|
238479
|
+
* pile. Used when a full drain is too expensive and the operator only
|
|
238480
|
+
* wants the recent window on the new disk. */
|
|
238481
|
+
sinceMs: zod.z.number().int().optional()
|
|
238110
238482
|
});
|
|
238111
238483
|
var StorageMigrationLeaseInputSchema = zod.z.object({ leaseId: zod.z.string().min(1) });
|
|
238112
238484
|
var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: zod.z.string().min(1) });
|
|
@@ -246609,6 +246981,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
246609
246981
|
"node-offline",
|
|
246610
246982
|
"node-inference-unavailable",
|
|
246611
246983
|
"detection-blind",
|
|
246984
|
+
"addon-crash-loop",
|
|
246612
246985
|
"addon-update-available",
|
|
246613
246986
|
"server-update-available",
|
|
246614
246987
|
"alarm-triggered",
|
|
@@ -246616,6 +246989,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
246616
246989
|
"alarm-disarmed",
|
|
246617
246990
|
"alarm-arming",
|
|
246618
246991
|
"alarm-arm-refused",
|
|
246992
|
+
"addon-updated",
|
|
246993
|
+
"server-updated",
|
|
246994
|
+
"export-completed",
|
|
246619
246995
|
"camera-online",
|
|
246620
246996
|
"camera-offline",
|
|
246621
246997
|
"camera-disabled",
|
|
@@ -247445,6 +247821,10 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
247445
247821
|
value: "detection-blind",
|
|
247446
247822
|
label: "Camera detecting nothing"
|
|
247447
247823
|
},
|
|
247824
|
+
{
|
|
247825
|
+
value: "addon-crash-loop",
|
|
247826
|
+
label: "Addon stopped after repeated crashes"
|
|
247827
|
+
},
|
|
247448
247828
|
{
|
|
247449
247829
|
value: "addon-update-available",
|
|
247450
247830
|
label: "Addon update available"
|
|
@@ -247453,6 +247833,18 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
247453
247833
|
value: "server-update-available",
|
|
247454
247834
|
label: "Server update available"
|
|
247455
247835
|
},
|
|
247836
|
+
{
|
|
247837
|
+
value: "addon-updated",
|
|
247838
|
+
label: "Addons updated"
|
|
247839
|
+
},
|
|
247840
|
+
{
|
|
247841
|
+
value: "server-updated",
|
|
247842
|
+
label: "Server updated"
|
|
247843
|
+
},
|
|
247844
|
+
{
|
|
247845
|
+
value: "export-completed",
|
|
247846
|
+
label: "Export completed"
|
|
247847
|
+
},
|
|
247456
247848
|
{
|
|
247457
247849
|
value: "alarm-arming",
|
|
247458
247850
|
label: "Alarm arming (exit delay)"
|
|
@@ -248469,7 +248861,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
248469
248861
|
* `'staging'`. */
|
|
248470
248862
|
markForTrain: zod.z.boolean().optional(),
|
|
248471
248863
|
/** Operator marked this track for diagnostic attention. */
|
|
248472
|
-
debug: zod.z.boolean().optional()
|
|
248864
|
+
debug: zod.z.boolean().optional(),
|
|
248865
|
+
/** Operator favourited this track. Pins it against pruning. */
|
|
248866
|
+
favourited: zod.z.boolean().optional()
|
|
248473
248867
|
};
|
|
248474
248868
|
var TrackRetrainFields = { retrainStatus: RetrainStatusSchema.optional() };
|
|
248475
248869
|
var TrackFlagsPatchSchema = zod.z.object(TrackFlagFields);
|
|
@@ -248477,6 +248871,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
248477
248871
|
trackId: zod.z.string(),
|
|
248478
248872
|
markForTrain: zod.z.boolean(),
|
|
248479
248873
|
debug: zod.z.boolean(),
|
|
248874
|
+
favourited: zod.z.boolean(),
|
|
248480
248875
|
/** The lifecycle state the boolean was derived from. Required here (unlike on
|
|
248481
248876
|
* a track row) because this shape is only ever produced by the write body,
|
|
248482
248877
|
* which always knows it — and a surface that has just written needs to render
|
|
@@ -248999,6 +249394,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
248999
249394
|
/** Per-track CLIP search vectors removed (best-effort). */
|
|
249000
249395
|
embeddings: zod.z.number().int()
|
|
249001
249396
|
});
|
|
249397
|
+
var DiskReconcileCountsSchema = zod.z.object({
|
|
249398
|
+
mediaDropped: zod.z.number().int(),
|
|
249399
|
+
tracks: zod.z.number().int(),
|
|
249400
|
+
events: zod.z.number().int()
|
|
249401
|
+
});
|
|
249002
249402
|
var EventStoreDeviceFootprintSchema = zod.z.object({
|
|
249003
249403
|
deviceId: zod.z.number(),
|
|
249004
249404
|
/** Persisted event rows (motion + object + audio) for the camera. */
|
|
@@ -249255,6 +249655,17 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
249255
249655
|
auth: "admin"
|
|
249256
249656
|
}),
|
|
249257
249657
|
/**
|
|
249658
|
+
* Disk-wins reconcile for one camera. Drops media index rows whose blobs
|
|
249659
|
+
* are gone, then cascades tracks (including favourited and staging) that
|
|
249660
|
+
* have no remaining files. Enrolled identity/vehicle/scene media is never
|
|
249661
|
+
* probed. Trackless motion/audio events with no remaining file are dropped,
|
|
249662
|
+
* including snapshot-less rows.
|
|
249663
|
+
*/
|
|
249664
|
+
reconcileFromDisk: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), DiskReconcileCountsSchema, {
|
|
249665
|
+
kind: "mutation",
|
|
249666
|
+
auth: "admin"
|
|
249667
|
+
}),
|
|
249668
|
+
/**
|
|
249258
249669
|
* Delete whole tracks (object events) by id for the given device,
|
|
249259
249670
|
* cascading their media in lockstep. Returns the number of tracks
|
|
249260
249671
|
* actually deleted plus the ids that could not be removed.
|
|
@@ -249273,7 +249684,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
249273
249684
|
auth: "admin"
|
|
249274
249685
|
}),
|
|
249275
249686
|
/**
|
|
249276
|
-
* Set the per-track operator flags (`markForTrain`, `debug`) on ONE track.
|
|
249687
|
+
* Set the per-track operator flags (`markForTrain`, `debug`, `favourited`) on ONE track.
|
|
249277
249688
|
* The patch is PARTIAL — an omitted key is left untouched — because the
|
|
249278
249689
|
* three surfaces that write it (admin Events grid, viewer track detail,
|
|
249279
249690
|
* viewer cluster detail) each own one toggle and must not clobber the other.
|
|
@@ -251261,6 +251672,24 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
251261
251672
|
/** Wall-clock ms spent on the stage before it was abandoned. */
|
|
251262
251673
|
elapsedMs: zod.z.number()
|
|
251263
251674
|
});
|
|
251675
|
+
var DiskReconcileJobSchema = zod.z.object({
|
|
251676
|
+
state: zod.z.enum([
|
|
251677
|
+
"idle",
|
|
251678
|
+
"running",
|
|
251679
|
+
"done",
|
|
251680
|
+
"error"
|
|
251681
|
+
]),
|
|
251682
|
+
total: zod.z.number().int().nonnegative(),
|
|
251683
|
+
completed: zod.z.number().int().nonnegative(),
|
|
251684
|
+
currentDeviceId: zod.z.number().int().nullable(),
|
|
251685
|
+
failed: zod.z.array(zod.z.number().int()).readonly(),
|
|
251686
|
+
mediaDropped: zod.z.number().int().nonnegative(),
|
|
251687
|
+
tracks: zod.z.number().int().nonnegative(),
|
|
251688
|
+
events: zod.z.number().int().nonnegative(),
|
|
251689
|
+
startedAtMs: zod.z.number().int().nullable(),
|
|
251690
|
+
finishedAtMs: zod.z.number().int().nullable(),
|
|
251691
|
+
error: zod.z.string().nullable()
|
|
251692
|
+
});
|
|
251264
251693
|
var CameraStatusSchema = zod.z.object({
|
|
251265
251694
|
deviceId: zod.z.number(),
|
|
251266
251695
|
assignment: CameraAssignmentStatusSchema,
|
|
@@ -251785,6 +252214,18 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
251785
252214
|
* rail without issuing N parallel browser round-trips.
|
|
251786
252215
|
*/
|
|
251787
252216
|
getCameraStatuses: require_sleep.method(zod.z.object({ deviceIds: zod.z.array(zod.z.number()).optional() }), zod.z.array(CameraStatusSchema).readonly()),
|
|
252217
|
+
/**
|
|
252218
|
+
* Disk-wins fleet reconcile after a recordings wipe. Starts the walk in
|
|
252219
|
+
* the addon process and returns immediately with the job snapshot — the
|
|
252220
|
+
* work outlives the tRPC/UDS 60s timeout. Poll `getReconcileFromDiskStatus`.
|
|
252221
|
+
* Admin-only. Idempotent while a job is already running.
|
|
252222
|
+
*/
|
|
252223
|
+
reconcileFromDisk: require_sleep.method(zod.z.void(), DiskReconcileJobSchema, {
|
|
252224
|
+
kind: "mutation",
|
|
252225
|
+
auth: "admin"
|
|
252226
|
+
}),
|
|
252227
|
+
/** Snapshot of the in-flight or last disk-wins fleet reconcile. */
|
|
252228
|
+
getReconcileFromDiskStatus: require_sleep.method(zod.z.void(), DiskReconcileJobSchema, { auth: "admin" }),
|
|
251788
252229
|
/** List every template the operator has saved. */
|
|
251789
252230
|
listTemplates: require_sleep.method(zod.z.void(), zod.z.array(PipelineTemplateSchema).readonly()),
|
|
251790
252231
|
/** Create a new named preset from a given CameraPipelineConfig. */
|
|
@@ -252927,6 +253368,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
252927
253368
|
stats: require_sleep.method(VectorStatsInputSchema, VectorStatsResultSchema)
|
|
252928
253369
|
}
|
|
252929
253370
|
};
|
|
253371
|
+
var MAX_CLIP_EVENT_IDS = 24;
|
|
252930
253372
|
var ClipSchema = zod.z.object({
|
|
252931
253373
|
/** Opaque, provider-namespaced id. The default provider encodes the time
|
|
252932
253374
|
* window so `getClipPlayback` is self-contained (no event re-query). */
|
|
@@ -252943,8 +253385,57 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
252943
253385
|
startMs: zod.z.number(),
|
|
252944
253386
|
endMs: zod.z.number()
|
|
252945
253387
|
}),
|
|
252946
|
-
/**
|
|
252947
|
-
thumbnail
|
|
253388
|
+
/**
|
|
253389
|
+
* Lazy thumbnail URL, never inlined.
|
|
253390
|
+
*
|
|
253391
|
+
* Recording-derived clips (events-mode keep-window, and the prepared
|
|
253392
|
+
* continuous event+fragment visit) MUST use the snapshot of the **main
|
|
253393
|
+
* event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
|
|
253394
|
+
* of the event that owns `kind` (object > motion > audio). Do not extract
|
|
253395
|
+
* a keyframe from the recorded segments. Other providers (onboard, HKSV)
|
|
253396
|
+
* mint their own stills.
|
|
253397
|
+
*
|
|
253398
|
+
* **VOUCHED, never fabricated.** A provider emits this only for an event it
|
|
253399
|
+
* has CONFIRMED owns at least one media row (its own, or its owning track's).
|
|
253400
|
+
* Absent is meaningful — "this visit has no event still" — never "we did not
|
|
253401
|
+
* look". Stamping it from a URL template made 35% of one camera's clips point
|
|
253402
|
+
* at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
|
|
253403
|
+
* A read that FAILS drops the claim; it never invents it.
|
|
253404
|
+
*/
|
|
253405
|
+
thumbnail: zod.z.string().optional(),
|
|
253406
|
+
/**
|
|
253407
|
+
* An instant INSIDE this visit's footage, hole-safe, where a recorded still
|
|
253408
|
+
* can be decoded. Present whenever the visit came from recorded availability;
|
|
253409
|
+
* absent on a per-event padded window (there is no footage to promise).
|
|
253410
|
+
*
|
|
253411
|
+
* This is not a thumbnail and not a second byte path: it is the argument to
|
|
253412
|
+
* the recorder's existing still route. The surface — never the provider —
|
|
253413
|
+
* decides whether to use it. It is the midpoint of the visit's LONGEST
|
|
253414
|
+
* contiguous range, not of the visit: a visit spans its holes by
|
|
253415
|
+
* construction, so a naive midpoint lands in dead air.
|
|
253416
|
+
*/
|
|
253417
|
+
stillAtMs: zod.z.number().optional(),
|
|
253418
|
+
/**
|
|
253419
|
+
* Analytics event ids that overlap this visit — a BOUNDED sample, newest
|
|
253420
|
+
* first within kind (object → motion → audio), capped at
|
|
253421
|
+
* {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
|
|
253422
|
+
*
|
|
253423
|
+
* Bounded because it is not a payload the surface pages through: one visit on
|
|
253424
|
+
* device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
|
|
253425
|
+
* camera-day. Read {@link eventCount} for the true total.
|
|
253426
|
+
*/
|
|
253427
|
+
eventIds: zod.z.array(zod.z.string()).optional(),
|
|
253428
|
+
/** How many analytics events actually overlap this visit. Differs from
|
|
253429
|
+
* `eventIds.length` exactly when the sample was capped — so a truncated
|
|
253430
|
+
* list is never mistaken for a quiet visit. */
|
|
253431
|
+
eventCount: zod.z.number().int().nonnegative().optional(),
|
|
253432
|
+
/** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
|
|
253433
|
+
* than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
|
|
253434
|
+
* bar keeps showing them via `recording.getAvailability`. */
|
|
253435
|
+
holes: zod.z.array(zod.z.object({
|
|
253436
|
+
startMs: zod.z.number(),
|
|
253437
|
+
endMs: zod.z.number()
|
|
253438
|
+
})).optional()
|
|
252948
253439
|
});
|
|
252949
253440
|
var ClipPlaybackSchema = zod.z.object({
|
|
252950
253441
|
/** HLS master URL through the hub data-plane (Range + token in path). */
|
|
@@ -253492,7 +253983,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
253492
253983
|
});
|
|
253493
253984
|
var AutoUpdateSettingsSchema = zod.z.object({
|
|
253494
253985
|
channel: ChannelSchema,
|
|
253495
|
-
intervalSeconds: zod.z.number()
|
|
253986
|
+
intervalSeconds: zod.z.number(),
|
|
253987
|
+
/**
|
|
253988
|
+
* Cadence of the "an update exists" POLLER, in seconds. Independent of
|
|
253989
|
+
* `channel`: the poller runs while auto-apply is `off`, because being told
|
|
253990
|
+
* about a publish and installing it are different decisions. Clamped
|
|
253991
|
+
* server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
|
|
253992
|
+
*/
|
|
253993
|
+
updateCheckIntervalSeconds: zod.z.number()
|
|
253496
253994
|
});
|
|
253497
253995
|
var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
253498
253996
|
var RestartAddonResultSchema = zod.z.unknown();
|
|
@@ -253743,7 +254241,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
253743
254241
|
getAutoUpdateSettings: require_sleep.method(zod.z.void(), AutoUpdateSettingsSchema, { auth: "admin" }),
|
|
253744
254242
|
setAutoUpdateSettings: require_sleep.method(zod.z.object({
|
|
253745
254243
|
channel: ChannelSchema,
|
|
253746
|
-
intervalSeconds: zod.z.number().min(300).max(86400).optional()
|
|
254244
|
+
intervalSeconds: zod.z.number().min(300).max(86400).optional(),
|
|
254245
|
+
/** Availability-poll cadence; see AutoUpdateSettingsSchema. */
|
|
254246
|
+
updateCheckIntervalSeconds: zod.z.number().min(900).max(604800).optional()
|
|
253747
254247
|
}), zod.z.unknown(), {
|
|
253748
254248
|
kind: "mutation",
|
|
253749
254249
|
auth: "admin"
|
|
@@ -259033,7 +259533,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
259033
259533
|
includeAudio: zod.z.boolean(),
|
|
259034
259534
|
maxLifeMs: zod.z.number().int().positive(),
|
|
259035
259535
|
deleteAfterDownload: zod.z.boolean(),
|
|
259036
|
-
title: zod.z.string().max(200).optional()
|
|
259536
|
+
title: zod.z.string().max(200).optional(),
|
|
259537
|
+
/** Notification-output target ids to ping when this export becomes ready. */
|
|
259538
|
+
notifyTargetIds: zod.z.array(zod.z.string().min(1)).max(20).optional()
|
|
259037
259539
|
}).superRefine((v, ctx) => {
|
|
259038
259540
|
if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
|
|
259039
259541
|
code: zod.z.ZodIssueCode.custom,
|
|
@@ -259083,20 +259585,30 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
259083
259585
|
name: zod.z.string(),
|
|
259084
259586
|
bytes: zod.z.number().int().nonnegative()
|
|
259085
259587
|
});
|
|
259588
|
+
var CreateExportInputSchema = zod.z.object({
|
|
259589
|
+
deviceId: zod.z.number(),
|
|
259590
|
+
/** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
|
|
259591
|
+
profile: zod.z.string().optional(),
|
|
259592
|
+
profiles: zod.z.array(zod.z.string()).min(1).optional(),
|
|
259593
|
+
fromMs: zod.z.number(),
|
|
259594
|
+
toMs: zod.z.number(),
|
|
259595
|
+
options: ExportOptionsSchema
|
|
259596
|
+
}).superRefine((v, ctx) => {
|
|
259597
|
+
if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
|
|
259598
|
+
code: zod.z.ZodIssueCode.custom,
|
|
259599
|
+
message: "pass profiles[] (min 1) or legacy profile",
|
|
259600
|
+
path: ["profiles"]
|
|
259601
|
+
});
|
|
259602
|
+
});
|
|
259086
259603
|
var recordingExportCapability = {
|
|
259087
259604
|
name: "recording-export",
|
|
259088
259605
|
scope: "system",
|
|
259089
259606
|
mode: "singleton",
|
|
259090
259607
|
methods: {
|
|
259091
|
-
/** Queue a render of `[fromMs,toMs)` for `deviceId`/`
|
|
259092
|
-
*
|
|
259093
|
-
|
|
259094
|
-
|
|
259095
|
-
profile: zod.z.string(),
|
|
259096
|
-
fromMs: zod.z.number(),
|
|
259097
|
-
toMs: zod.z.number(),
|
|
259098
|
-
options: ExportOptionsSchema
|
|
259099
|
-
}), ExportRecordSchema, {
|
|
259608
|
+
/** Queue a render of `[fromMs,toMs)` for `deviceId`/`profiles` (legacy
|
|
259609
|
+
* singular `profile` still accepted). Fails fast when no footage covers
|
|
259610
|
+
* the range. One job per profile; returns the first queued record. */
|
|
259611
|
+
createExport: require_sleep.method(CreateExportInputSchema, ExportRecordSchema, {
|
|
259100
259612
|
kind: "mutation",
|
|
259101
259613
|
auth: "protected"
|
|
259102
259614
|
}),
|
|
@@ -262006,15 +262518,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
262006
262518
|
labels: ["probe not implemented"]
|
|
262007
262519
|
};
|
|
262008
262520
|
}
|
|
262009
|
-
/**
|
|
262010
|
-
* Top-level devices restored at once in {@link onRestoreDevices}.
|
|
262011
|
-
*
|
|
262012
|
-
* Four covers the fleets this ships to without turning a boot into a burst a
|
|
262013
|
-
* camera NVR answers with a refusal. A provider whose upstream is a single
|
|
262014
|
-
* session with a serial command channel (a Baichuan hub, an NVR that
|
|
262015
|
-
* serialises ISAPI) should lower it; nothing needs to raise it.
|
|
262016
|
-
*/
|
|
262017
|
-
restoreConcurrency = 4;
|
|
262018
262521
|
async restoreDevices(savedDevices) {
|
|
262019
262522
|
await this.onRestoreDevices(savedDevices);
|
|
262020
262523
|
if (savedDevices.length > 0) this.ctx.logger.info(`Restored ${savedDevices.length} ${this.providerName} device(s)`);
|
|
@@ -262069,14 +262572,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
262069
262572
|
});
|
|
262070
262573
|
}
|
|
262071
262574
|
};
|
|
262072
|
-
|
|
262073
|
-
await Promise.all(Array.from({ length: Math.min(Math.max(1, this.restoreConcurrency), topLevel.length) }, async () => {
|
|
262074
|
-
for (; ; ) {
|
|
262075
|
-
const saved = topLevel[nextTopLevel++];
|
|
262076
|
-
if (saved === void 0) return;
|
|
262077
|
-
await restoreOne(saved);
|
|
262078
|
-
}
|
|
262079
|
-
}));
|
|
262575
|
+
await Promise.all(topLevel.map((saved) => restoreOne(saved)));
|
|
262080
262576
|
const childRows = savedDevices.filter((s) => s.parentDeviceId !== null);
|
|
262081
262577
|
for (const saved of childRows) {
|
|
262082
262578
|
const Class = this.deviceClasses[saved.type];
|
|
@@ -267639,6 +268135,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
267639
268135
|
addonId: null,
|
|
267640
268136
|
access: "create"
|
|
267641
268137
|
},
|
|
268138
|
+
"pipelineAnalytics.reconcileFromDisk": {
|
|
268139
|
+
capName: "pipeline-analytics",
|
|
268140
|
+
capScope: "device",
|
|
268141
|
+
addonId: null,
|
|
268142
|
+
access: "create"
|
|
268143
|
+
},
|
|
267642
268144
|
"pipelineAnalytics.refreshStorageLocationsForMigration": {
|
|
267643
268145
|
capName: "pipeline-analytics",
|
|
267644
268146
|
capScope: "device",
|
|
@@ -268041,6 +268543,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
268041
268543
|
addonId: null,
|
|
268042
268544
|
access: "view"
|
|
268043
268545
|
},
|
|
268546
|
+
"pipelineOrchestrator.getReconcileFromDiskStatus": {
|
|
268547
|
+
capName: "pipeline-orchestrator",
|
|
268548
|
+
capScope: "system",
|
|
268549
|
+
addonId: null,
|
|
268550
|
+
access: "view"
|
|
268551
|
+
},
|
|
268044
268552
|
"pipelineOrchestrator.listAgentSettings": {
|
|
268045
268553
|
capName: "pipeline-orchestrator",
|
|
268046
268554
|
capScope: "system",
|
|
@@ -268065,6 +268573,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
268065
268573
|
addonId: null,
|
|
268066
268574
|
access: "create"
|
|
268067
268575
|
},
|
|
268576
|
+
"pipelineOrchestrator.reconcileFromDisk": {
|
|
268577
|
+
capName: "pipeline-orchestrator",
|
|
268578
|
+
capScope: "system",
|
|
268579
|
+
addonId: null,
|
|
268580
|
+
access: "create"
|
|
268581
|
+
},
|
|
268068
268582
|
"pipelineOrchestrator.removeAgentSettings": {
|
|
268069
268583
|
capName: "pipeline-orchestrator",
|
|
268070
268584
|
capScope: "system",
|
|
@@ -271308,6 +271822,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
271308
271822
|
form: "single",
|
|
271309
271823
|
optional: true
|
|
271310
271824
|
}],
|
|
271825
|
+
"pipelineAnalytics.reconcileFromDisk": [{
|
|
271826
|
+
name: "deviceId",
|
|
271827
|
+
form: "single",
|
|
271828
|
+
optional: false
|
|
271829
|
+
}],
|
|
271311
271830
|
"pipelineAnalytics.restageRetrainTrack": [{
|
|
271312
271831
|
name: "deviceId",
|
|
271313
271832
|
form: "single",
|
|
@@ -272926,6 +273445,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
272926
273445
|
getNodeInferenceDevices: (input) => dispatch("pipelineOrchestrator", "getNodeInferenceDevices", "query", input),
|
|
272927
273446
|
resetNodePipelineDefaults: (input) => dispatch("pipelineOrchestrator", "resetNodePipelineDefaults", "mutation", input),
|
|
272928
273447
|
getCameraStatuses: (input) => dispatch("pipelineOrchestrator", "getCameraStatuses", "query", input),
|
|
273448
|
+
reconcileFromDisk: (input) => dispatch("pipelineOrchestrator", "reconcileFromDisk", "mutation", input),
|
|
273449
|
+
getReconcileFromDiskStatus: (input) => dispatch("pipelineOrchestrator", "getReconcileFromDiskStatus", "query", input),
|
|
272929
273450
|
listTemplates: (input) => dispatch("pipelineOrchestrator", "listTemplates", "query", input),
|
|
272930
273451
|
saveTemplate: (input) => dispatch("pipelineOrchestrator", "saveTemplate", "mutation", input),
|
|
272931
273452
|
updateTemplate: (input) => dispatch("pipelineOrchestrator", "updateTemplate", "mutation", input),
|
|
@@ -272974,6 +273495,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
272974
273495
|
startStorageRebalance: (input) => dispatch("recording", "startStorageRebalance", "mutation", input)
|
|
272975
273496
|
},
|
|
272976
273497
|
recordingExport: {
|
|
273498
|
+
createExport: (input) => dispatch("recordingExport", "createExport", "mutation", input),
|
|
272977
273499
|
getExport: (input) => dispatch("recordingExport", "getExport", "query", input),
|
|
272978
273500
|
cancelExport: (input) => dispatch("recordingExport", "cancelExport", "mutation", input),
|
|
272979
273501
|
deleteExport: (input) => dispatch("recordingExport", "deleteExport", "mutation", input),
|
|
@@ -273542,6 +274064,27 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
273542
274064
|
}
|
|
273543
274065
|
return schedule.invert === true ? !inside : inside;
|
|
273544
274066
|
}
|
|
274067
|
+
var NC_SYSTEM_EVENT_FILTER_KEYS = [
|
|
274068
|
+
"deviceIds",
|
|
274069
|
+
"deviceTypes",
|
|
274070
|
+
"nodeIds",
|
|
274071
|
+
"packageNames"
|
|
274072
|
+
];
|
|
274073
|
+
function systemEventFilterApplies(kind, filter) {
|
|
274074
|
+
switch (filter) {
|
|
274075
|
+
case "deviceIds":
|
|
274076
|
+
return kind.startsWith("device-") || kind.startsWith("stream-") || kind === "detection-blind" || kind === "alarm-triggered" || kind === "export-completed";
|
|
274077
|
+
case "deviceTypes":
|
|
274078
|
+
return kind.startsWith("device-") || kind === "detection-blind";
|
|
274079
|
+
case "nodeIds":
|
|
274080
|
+
return kind.startsWith("node-") || kind === "addon-crash-loop" || kind === "addon-update-available" || kind === "addon-updated" || kind === "server-update-available" || kind === "server-updated";
|
|
274081
|
+
case "packageNames":
|
|
274082
|
+
return kind.endsWith("update-available") || kind === "addon-updated" || kind === "server-updated";
|
|
274083
|
+
}
|
|
274084
|
+
}
|
|
274085
|
+
function systemEventFilterAppliesToAnyKind(kinds, filter) {
|
|
274086
|
+
return kinds.some((kind) => systemEventFilterApplies(kind, filter));
|
|
274087
|
+
}
|
|
273545
274088
|
var TimelapseTemplateSchema = zod.z.object({
|
|
273546
274089
|
title: zod.z.string().max(500).optional(),
|
|
273547
274090
|
body: zod.z.string().max(2e3).optional()
|
|
@@ -274592,6 +275135,278 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
274592
275135
|
return rawUrl;
|
|
274593
275136
|
}
|
|
274594
275137
|
}
|
|
275138
|
+
var MB = 1024 * 1024;
|
|
275139
|
+
var DEFAULT_POOL_MEMORY_POLICY = {
|
|
275140
|
+
sampleIntervalMs: 6e4,
|
|
275141
|
+
baselineSettleSamples: 5,
|
|
275142
|
+
baselineSampleCount: 3,
|
|
275143
|
+
restartMultiple: 4,
|
|
275144
|
+
floorBytes: 1024 * MB,
|
|
275145
|
+
ceilingBytes: 3072 * MB,
|
|
275146
|
+
cooldownMs: 30 * 6e4,
|
|
275147
|
+
maxRestartsPerWindow: 6,
|
|
275148
|
+
restartWindowMs: 1440 * 6e4
|
|
275149
|
+
};
|
|
275150
|
+
function resolvePoolMemoryPolicy(env) {
|
|
275151
|
+
const num = (key, fallback, min) => {
|
|
275152
|
+
const raw = env[key];
|
|
275153
|
+
if (raw === void 0) return fallback;
|
|
275154
|
+
const parsed = Number(raw);
|
|
275155
|
+
return Number.isFinite(parsed) && parsed >= min ? parsed : fallback;
|
|
275156
|
+
};
|
|
275157
|
+
const d = DEFAULT_POOL_MEMORY_POLICY;
|
|
275158
|
+
return {
|
|
275159
|
+
sampleIntervalMs: num("CAMSTACK_POOL_MEM_INTERVAL_MS", d.sampleIntervalMs, 5e3),
|
|
275160
|
+
baselineSettleSamples: d.baselineSettleSamples,
|
|
275161
|
+
baselineSampleCount: d.baselineSampleCount,
|
|
275162
|
+
restartMultiple: num("CAMSTACK_POOL_MEM_MULTIPLE", d.restartMultiple, 1.5),
|
|
275163
|
+
floorBytes: num("CAMSTACK_POOL_MEM_FLOOR_MB", d.floorBytes / MB, 128) * MB,
|
|
275164
|
+
ceilingBytes: num("CAMSTACK_POOL_MEM_CEILING_MB", d.ceilingBytes / MB, 256) * MB,
|
|
275165
|
+
cooldownMs: num("CAMSTACK_POOL_MEM_COOLDOWN_MS", d.cooldownMs, 6e4),
|
|
275166
|
+
maxRestartsPerWindow: num("CAMSTACK_POOL_MEM_MAX_RESTARTS", d.maxRestartsPerWindow, 1),
|
|
275167
|
+
restartWindowMs: num("CAMSTACK_POOL_MEM_RESTART_WINDOW_MS", d.restartWindowMs, 6e4)
|
|
275168
|
+
};
|
|
275169
|
+
}
|
|
275170
|
+
function parseProcStatus(text) {
|
|
275171
|
+
const kb = (label) => {
|
|
275172
|
+
const match = text.match(new RegExp(`^${label}:\\s+(\\d+)\\s*kB`, "m"));
|
|
275173
|
+
return match ? Number(match[1]) * 1024 : null;
|
|
275174
|
+
};
|
|
275175
|
+
const rssBytes = kb("VmRSS");
|
|
275176
|
+
if (rssBytes === null) return null;
|
|
275177
|
+
const threadsMatch = text.match(/^Threads:\s+(\d+)/m);
|
|
275178
|
+
return {
|
|
275179
|
+
rssBytes,
|
|
275180
|
+
vmBytes: kb("VmSize") ?? 0,
|
|
275181
|
+
hwmBytes: kb("VmHWM") ?? 0,
|
|
275182
|
+
swapBytes: kb("VmSwap") ?? 0,
|
|
275183
|
+
threads: threadsMatch ? Number(threadsMatch[1]) : 0
|
|
275184
|
+
};
|
|
275185
|
+
}
|
|
275186
|
+
function initialPoolMemoryState() {
|
|
275187
|
+
return {
|
|
275188
|
+
settleSeen: 0,
|
|
275189
|
+
baselineWindow: [],
|
|
275190
|
+
baselineBytes: null,
|
|
275191
|
+
restartsAt: [],
|
|
275192
|
+
lastRestartAt: null
|
|
275193
|
+
};
|
|
275194
|
+
}
|
|
275195
|
+
function median(values) {
|
|
275196
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
275197
|
+
return sorted[Math.floor(sorted.length / 2)];
|
|
275198
|
+
}
|
|
275199
|
+
function advanceBaseline(state, rssBytes, policy) {
|
|
275200
|
+
if (state.baselineBytes !== null) return state;
|
|
275201
|
+
if (state.settleSeen < policy.baselineSettleSamples) return {
|
|
275202
|
+
...state,
|
|
275203
|
+
settleSeen: state.settleSeen + 1
|
|
275204
|
+
};
|
|
275205
|
+
const window2 = [...state.baselineWindow, rssBytes];
|
|
275206
|
+
if (window2.length < policy.baselineSampleCount) return {
|
|
275207
|
+
...state,
|
|
275208
|
+
baselineWindow: window2
|
|
275209
|
+
};
|
|
275210
|
+
return {
|
|
275211
|
+
...state,
|
|
275212
|
+
baselineWindow: window2,
|
|
275213
|
+
baselineBytes: median(window2)
|
|
275214
|
+
};
|
|
275215
|
+
}
|
|
275216
|
+
function poolMemoryThreshold(baselineBytes, policy) {
|
|
275217
|
+
if (baselineBytes === null) return policy.ceilingBytes;
|
|
275218
|
+
return Math.min(policy.ceilingBytes, Math.max(policy.floorBytes, policy.restartMultiple * baselineBytes));
|
|
275219
|
+
}
|
|
275220
|
+
function pruneRestarts(restartsAt, nowMs, policy) {
|
|
275221
|
+
return restartsAt.filter((t) => nowMs - t < policy.restartWindowMs);
|
|
275222
|
+
}
|
|
275223
|
+
function evaluatePoolMemory(state, rssBytes, nowMs, policy) {
|
|
275224
|
+
const next = advanceBaseline(state, rssBytes, policy);
|
|
275225
|
+
const thresholdBytes = poolMemoryThreshold(next.baselineBytes, policy);
|
|
275226
|
+
if (rssBytes <= thresholdBytes) return {
|
|
275227
|
+
state: next,
|
|
275228
|
+
action: next.baselineBytes === null ? "baseline-pending" : "ok",
|
|
275229
|
+
baselineBytes: next.baselineBytes,
|
|
275230
|
+
thresholdBytes
|
|
275231
|
+
};
|
|
275232
|
+
const inWindow = pruneRestarts(next.restartsAt, nowMs, policy);
|
|
275233
|
+
const pruned = {
|
|
275234
|
+
...next,
|
|
275235
|
+
restartsAt: inWindow
|
|
275236
|
+
};
|
|
275237
|
+
if (inWindow.length >= policy.maxRestartsPerWindow) return {
|
|
275238
|
+
state: pruned,
|
|
275239
|
+
action: "exhausted",
|
|
275240
|
+
baselineBytes: next.baselineBytes,
|
|
275241
|
+
thresholdBytes
|
|
275242
|
+
};
|
|
275243
|
+
if (pruned.lastRestartAt !== null && nowMs - pruned.lastRestartAt < policy.cooldownMs) return {
|
|
275244
|
+
state: pruned,
|
|
275245
|
+
action: "cooldown",
|
|
275246
|
+
baselineBytes: next.baselineBytes,
|
|
275247
|
+
thresholdBytes
|
|
275248
|
+
};
|
|
275249
|
+
return {
|
|
275250
|
+
state: pruned,
|
|
275251
|
+
action: "restart",
|
|
275252
|
+
baselineBytes: next.baselineBytes,
|
|
275253
|
+
thresholdBytes
|
|
275254
|
+
};
|
|
275255
|
+
}
|
|
275256
|
+
function commitWatchdogRestart(state, nowMs) {
|
|
275257
|
+
return {
|
|
275258
|
+
...initialPoolMemoryState(),
|
|
275259
|
+
restartsAt: [...state.restartsAt, nowMs],
|
|
275260
|
+
lastRestartAt: nowMs
|
|
275261
|
+
};
|
|
275262
|
+
}
|
|
275263
|
+
function resetPoolBaseline(state) {
|
|
275264
|
+
return {
|
|
275265
|
+
...initialPoolMemoryState(),
|
|
275266
|
+
restartsAt: state.restartsAt,
|
|
275267
|
+
lastRestartAt: state.lastRestartAt
|
|
275268
|
+
};
|
|
275269
|
+
}
|
|
275270
|
+
function pickRestartCandidate(candidates) {
|
|
275271
|
+
if (candidates.length === 0) return null;
|
|
275272
|
+
let worst = candidates[0];
|
|
275273
|
+
for (const c of candidates) if (c.rssBytes / c.thresholdBytes > worst.rssBytes / worst.thresholdBytes) worst = c;
|
|
275274
|
+
return worst.key;
|
|
275275
|
+
}
|
|
275276
|
+
var PoolMemoryWatchdog = class {
|
|
275277
|
+
pools = /* @__PURE__ */ new Map();
|
|
275278
|
+
timer = null;
|
|
275279
|
+
sweeping = false;
|
|
275280
|
+
stopped = false;
|
|
275281
|
+
opts;
|
|
275282
|
+
now;
|
|
275283
|
+
setTimer;
|
|
275284
|
+
clearTimer;
|
|
275285
|
+
constructor(opts) {
|
|
275286
|
+
this.opts = opts;
|
|
275287
|
+
this.now = opts.now ?? (() => Date.now());
|
|
275288
|
+
this.setTimer = opts.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
275289
|
+
this.clearTimer = opts.clearTimer ?? ((t) => clearTimeout(t));
|
|
275290
|
+
}
|
|
275291
|
+
start() {
|
|
275292
|
+
if (this.timer || this.stopped) return;
|
|
275293
|
+
this.arm();
|
|
275294
|
+
}
|
|
275295
|
+
stop() {
|
|
275296
|
+
this.stopped = true;
|
|
275297
|
+
if (this.timer) {
|
|
275298
|
+
this.clearTimer(this.timer);
|
|
275299
|
+
this.timer = null;
|
|
275300
|
+
}
|
|
275301
|
+
}
|
|
275302
|
+
arm() {
|
|
275303
|
+
const timer = this.setTimer(() => {
|
|
275304
|
+
this.sweep().finally(() => {
|
|
275305
|
+
if (!this.stopped) this.arm();
|
|
275306
|
+
});
|
|
275307
|
+
}, this.opts.policy.sampleIntervalMs);
|
|
275308
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
275309
|
+
this.timer = timer;
|
|
275310
|
+
}
|
|
275311
|
+
/** One sweep: sample → log every pool → restart at most one. Public so the
|
|
275312
|
+
* loop is testable without fake global timers. */
|
|
275313
|
+
async sweep() {
|
|
275314
|
+
if (this.sweeping) return;
|
|
275315
|
+
this.sweeping = true;
|
|
275316
|
+
try {
|
|
275317
|
+
await this.doSweep();
|
|
275318
|
+
} catch (err) {
|
|
275319
|
+
this.opts.log.warn("pool memory sweep failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
275320
|
+
} finally {
|
|
275321
|
+
this.sweeping = false;
|
|
275322
|
+
}
|
|
275323
|
+
}
|
|
275324
|
+
async doSweep() {
|
|
275325
|
+
const nowMs = this.now();
|
|
275326
|
+
const samples = await this.opts.sample();
|
|
275327
|
+
const candidates = [];
|
|
275328
|
+
const verdictByKey = /* @__PURE__ */ new Map();
|
|
275329
|
+
for (const t of samples) {
|
|
275330
|
+
const tracked2 = this.pools.get(t.key);
|
|
275331
|
+
let state = tracked2?.state ?? initialPoolMemoryState();
|
|
275332
|
+
if (tracked2 && tracked2.pids.length > 0 && !samePids(tracked2.pids, t.pids)) {
|
|
275333
|
+
this.opts.log.warn("pool process changed outside the watchdog \u2014 re-baselining", { meta: {
|
|
275334
|
+
poolKey: t.key,
|
|
275335
|
+
previousPids: tracked2.pids,
|
|
275336
|
+
pids: t.pids
|
|
275337
|
+
} });
|
|
275338
|
+
state = resetPoolBaseline(state);
|
|
275339
|
+
}
|
|
275340
|
+
const verdict2 = evaluatePoolMemory(state, t.rssBytes, nowMs, this.opts.policy);
|
|
275341
|
+
this.pools.set(t.key, {
|
|
275342
|
+
state: verdict2.state,
|
|
275343
|
+
pids: t.pids
|
|
275344
|
+
});
|
|
275345
|
+
verdictByKey.set(t.key, verdict2);
|
|
275346
|
+
this.opts.log.info("pool memory", { meta: {
|
|
275347
|
+
poolKey: t.key,
|
|
275348
|
+
pids: t.pids,
|
|
275349
|
+
rssMb: Math.round(t.rssBytes / MB),
|
|
275350
|
+
baselineMb: verdict2.baselineBytes === null ? null : Math.round(verdict2.baselineBytes / MB),
|
|
275351
|
+
thresholdMb: Math.round(verdict2.thresholdBytes / MB),
|
|
275352
|
+
action: verdict2.action,
|
|
275353
|
+
...t.meta
|
|
275354
|
+
} });
|
|
275355
|
+
if (verdict2.action === "restart") candidates.push({
|
|
275356
|
+
key: t.key,
|
|
275357
|
+
rssBytes: t.rssBytes,
|
|
275358
|
+
thresholdBytes: verdict2.thresholdBytes
|
|
275359
|
+
});
|
|
275360
|
+
else if (verdict2.action === "cooldown") this.opts.log.warn("pool over memory threshold but inside restart cooldown", { meta: {
|
|
275361
|
+
poolKey: t.key,
|
|
275362
|
+
rssMb: Math.round(t.rssBytes / MB),
|
|
275363
|
+
thresholdMb: Math.round(verdict2.thresholdBytes / MB),
|
|
275364
|
+
cooldownMs: this.opts.policy.cooldownMs
|
|
275365
|
+
} });
|
|
275366
|
+
else if (verdict2.action === "exhausted") this.opts.log.error("pool memory watchdog gave up \u2014 restart budget exhausted, pool leaks faster than restarts can pay for; needs a heap profile (py-spy / tracemalloc)", { meta: {
|
|
275367
|
+
poolKey: t.key,
|
|
275368
|
+
rssMb: Math.round(t.rssBytes / MB),
|
|
275369
|
+
thresholdMb: Math.round(verdict2.thresholdBytes / MB),
|
|
275370
|
+
maxRestartsPerWindow: this.opts.policy.maxRestartsPerWindow,
|
|
275371
|
+
restartWindowMs: this.opts.policy.restartWindowMs
|
|
275372
|
+
} });
|
|
275373
|
+
}
|
|
275374
|
+
for (const key of [...this.pools.keys()]) if (!samples.some((t) => t.key === key)) this.pools.delete(key);
|
|
275375
|
+
const pickedKey = pickRestartCandidate(candidates);
|
|
275376
|
+
if (pickedKey === null) return;
|
|
275377
|
+
const picked = candidates.find((c) => c.key === pickedKey);
|
|
275378
|
+
const verdict = verdictByKey.get(pickedKey);
|
|
275379
|
+
const tracked = this.pools.get(pickedKey);
|
|
275380
|
+
const restartsInWindow = tracked.state.restartsAt.length + 1;
|
|
275381
|
+
this.opts.log.warn("pool memory watchdog restarting pool \u2014 RSS over threshold", { meta: {
|
|
275382
|
+
poolKey: pickedKey,
|
|
275383
|
+
pids: tracked.pids,
|
|
275384
|
+
rssMb: Math.round(picked.rssBytes / MB),
|
|
275385
|
+
thresholdMb: Math.round(picked.thresholdBytes / MB),
|
|
275386
|
+
baselineMb: verdict.baselineBytes === null ? null : Math.round(verdict.baselineBytes / MB),
|
|
275387
|
+
restartsInWindow,
|
|
275388
|
+
deferredCandidates: candidates.filter((c) => c.key !== pickedKey).map((c) => c.key)
|
|
275389
|
+
} });
|
|
275390
|
+
try {
|
|
275391
|
+
await this.opts.restart(pickedKey);
|
|
275392
|
+
this.pools.set(pickedKey, {
|
|
275393
|
+
state: commitWatchdogRestart(tracked.state, this.now()),
|
|
275394
|
+
pids: []
|
|
275395
|
+
});
|
|
275396
|
+
} catch (err) {
|
|
275397
|
+
this.opts.log.error("pool memory watchdog restart failed", { meta: {
|
|
275398
|
+
poolKey: pickedKey,
|
|
275399
|
+
error: err instanceof Error ? err.message : String(err)
|
|
275400
|
+
} });
|
|
275401
|
+
}
|
|
275402
|
+
}
|
|
275403
|
+
};
|
|
275404
|
+
function samePids(a, b) {
|
|
275405
|
+
if (a.length !== b.length) return false;
|
|
275406
|
+
const sortedA = [...a].sort((x, y) => x - y);
|
|
275407
|
+
const sortedB = [...b].sort((x, y) => x - y);
|
|
275408
|
+
return sortedA.every((v, i) => v === sortedB[i]);
|
|
275409
|
+
}
|
|
274595
275410
|
function rectsToCells(rects, gridWidth, gridHeight) {
|
|
274596
275411
|
const total = gridWidth * gridHeight;
|
|
274597
275412
|
if (total <= 0 || rects.length === 0) return Array.from({ length: Math.max(0, total) }).fill(false);
|
|
@@ -275266,6 +276081,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
275266
276081
|
exports.DEFAULT_EVENT_COLOR = DEFAULT_EVENT_COLOR;
|
|
275267
276082
|
exports.DEFAULT_FEATURES = DEFAULT_FEATURES;
|
|
275268
276083
|
exports.DEFAULT_NATIVE_LEASE_SETTINGS = DEFAULT_NATIVE_LEASE_SETTINGS;
|
|
276084
|
+
exports.DEFAULT_POOL_MEMORY_POLICY = DEFAULT_POOL_MEMORY_POLICY;
|
|
276085
|
+
exports.DEFAULT_RECORDING_PROFILES = DEFAULT_RECORDING_PROFILES;
|
|
275269
276086
|
exports.DEFAULT_RETENTION = DEFAULT_RETENTION;
|
|
275270
276087
|
exports.DEFAULT_RUNTIME_STATE_DURABILITY = require_sleep.DEFAULT_RUNTIME_STATE_DURABILITY;
|
|
275271
276088
|
exports.DEFAULT_SCRUB_THUMBNAIL_PRESET = DEFAULT_SCRUB_THUMBNAIL_PRESET;
|
|
@@ -275316,6 +276133,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
275316
276133
|
exports.DiscoveredChildStatusSchema = DiscoveredChildStatusSchema;
|
|
275317
276134
|
exports.DiscoveredDeviceSchema = DiscoveredDeviceSchema;
|
|
275318
276135
|
exports.DiscoveredTargetSchema = DiscoveredTargetSchema;
|
|
276136
|
+
exports.DiskReconcileJobSchema = DiskReconcileJobSchema;
|
|
275319
276137
|
exports.DisposerChain = require_sleep.DisposerChain;
|
|
275320
276138
|
exports.DoorbellPressEventSchema = DoorbellPressEventSchema;
|
|
275321
276139
|
exports.DoorbellStatusSchema = DoorbellStatusSchema;
|
|
@@ -275455,6 +276273,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
275455
276273
|
exports.LoginMethodContributionSchema = LoginMethodContributionSchema;
|
|
275456
276274
|
exports.LoginStageEnum = LoginStageEnum;
|
|
275457
276275
|
exports.MACRO_LABELS = MACRO_LABELS;
|
|
276276
|
+
exports.MAX_CLIP_EVENT_IDS = MAX_CLIP_EVENT_IDS;
|
|
275458
276277
|
exports.MAX_CONDITION_DEPTH = MAX_CONDITION_DEPTH;
|
|
275459
276278
|
exports.MAX_CONDITION_LEAVES = MAX_CONDITION_LEAVES;
|
|
275460
276279
|
exports.MAX_EXPRESSION_AST_NODES = MAX_EXPRESSION_AST_NODES;
|
|
@@ -275546,6 +276365,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
275546
276365
|
exports.NC_HISTORY_LIMIT_MAX = NC_HISTORY_LIMIT_MAX;
|
|
275547
276366
|
exports.NC_MAX_PER_TRACK_IMMEDIATE = NC_MAX_PER_TRACK_IMMEDIATE;
|
|
275548
276367
|
exports.NC_SNOOZE_MAX_MINUTES = NC_SNOOZE_MAX_MINUTES;
|
|
276368
|
+
exports.NC_SYSTEM_EVENT_FILTER_KEYS = NC_SYSTEM_EVENT_FILTER_KEYS;
|
|
275549
276369
|
exports.NC_TAXONOMY = NC_TAXONOMY;
|
|
275550
276370
|
exports.NativeCropBboxSchema = NativeCropBboxSchema;
|
|
275551
276371
|
exports.NativeCropRefSchema = NativeCropRefSchema;
|
|
@@ -275659,6 +276479,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
275659
276479
|
exports.PipelineValidationResultSchema = PipelineValidationResultSchema;
|
|
275660
276480
|
exports.PlaceholderReasonSchema = PlaceholderReasonSchema;
|
|
275661
276481
|
exports.PolygonPointSchema = PolygonPointSchema;
|
|
276482
|
+
exports.PoolMemoryWatchdog = PoolMemoryWatchdog;
|
|
275662
276483
|
exports.PowerMeterStatusSchema = PowerMeterStatusSchema;
|
|
275663
276484
|
exports.PresenceStatusSchema = PresenceStatusSchema;
|
|
275664
276485
|
exports.PressureSensorStatusSchema = PressureSensorStatusSchema;
|
|
@@ -275913,6 +276734,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
275913
276734
|
exports.UpdateUserInputSchema = UpdateUserInputSchema;
|
|
275914
276735
|
exports.UserRecordSchema = UserRecordSchema;
|
|
275915
276736
|
exports.UserSummarySchema = UserSummarySchema;
|
|
276737
|
+
exports.VISIT_MERGE_GAP_MS = VISIT_MERGE_GAP_MS;
|
|
275916
276738
|
exports.VacuumControlStatusSchema = VacuumControlStatusSchema;
|
|
275917
276739
|
exports.VacuumStateSchema = VacuumStateSchema;
|
|
275918
276740
|
exports.ValveStateSchema = ValveStateSchema;
|
|
@@ -276025,6 +276847,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276025
276847
|
exports.collectHydratedFieldValues = require_sleep.collectHydratedFieldValues;
|
|
276026
276848
|
exports.colorCapability = colorCapability;
|
|
276027
276849
|
exports.colorForKind = colorForKind;
|
|
276850
|
+
exports.commitWatchdogRestart = commitWatchdogRestart;
|
|
276028
276851
|
exports.compileExpression = compileExpression;
|
|
276029
276852
|
exports.compileExpressionSafe = compileExpressionSafe;
|
|
276030
276853
|
exports.composeSwitchedOff = composeSwitchedOff;
|
|
@@ -276094,6 +276917,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276094
276917
|
exports.errMsg = require_err_msg.errMsg;
|
|
276095
276918
|
exports.evaluateAst = evaluateAst;
|
|
276096
276919
|
exports.evaluateExpressionSource = evaluateExpressionSource;
|
|
276920
|
+
exports.evaluatePoolMemory = evaluatePoolMemory;
|
|
276097
276921
|
exports.evaluateZoneRules = evaluateZoneRules;
|
|
276098
276922
|
exports.event = require_sleep.event;
|
|
276099
276923
|
exports.eventEmitterCapability = eventEmitterCapability;
|
|
@@ -276123,6 +276947,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276123
276947
|
exports.hydrateSchema = require_sleep.hydrateSchema;
|
|
276124
276948
|
exports.imageCapability = imageCapability;
|
|
276125
276949
|
exports.imageSettingsCapability = imageSettingsCapability;
|
|
276950
|
+
exports.initialPoolMemoryState = initialPoolMemoryState;
|
|
276126
276951
|
exports.integrationsCapability = integrationsCapability;
|
|
276127
276952
|
exports.intercomCapability = intercomCapability;
|
|
276128
276953
|
exports.invocationFromEncodeProfile = require_canonical_hash.invocationFromEncodeProfile;
|
|
@@ -276205,6 +277030,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276205
277030
|
exports.parseJsonArray = require_sleep.parseJsonArray;
|
|
276206
277031
|
exports.parseJsonObject = require_sleep.parseJsonObject;
|
|
276207
277032
|
exports.parseJsonUnknown = require_sleep.parseJsonUnknown;
|
|
277033
|
+
exports.parseProcStatus = parseProcStatus;
|
|
276208
277034
|
exports.parseProfileBrokerId = require_sleep.parseProfileBrokerId;
|
|
276209
277035
|
exports.parseStreamParamsFormPatch = parseStreamParamsFormPatch;
|
|
276210
277036
|
exports.patchAudio = patchAudio;
|
|
@@ -276213,6 +277039,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276213
277039
|
exports.pickDetailCropConvention = pickDetailCropConvention;
|
|
276214
277040
|
exports.pickNativeLeaseOverride = pickNativeLeaseOverride;
|
|
276215
277041
|
exports.pickPreferredRtspEntry = pickPreferredRtspEntry;
|
|
277042
|
+
exports.pickRestartCandidate = pickRestartCandidate;
|
|
276216
277043
|
exports.pickVideoEncoder = require_canonical_hash.pickVideoEncoder;
|
|
276217
277044
|
exports.pickerForCondition = pickerForCondition;
|
|
276218
277045
|
exports.pipelineAnalyticsCapability = pipelineAnalyticsCapability;
|
|
@@ -276221,6 +277048,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276221
277048
|
exports.pipelineRunnerCapability = pipelineRunnerCapability;
|
|
276222
277049
|
exports.plateGalleryCapability = plateGalleryCapability;
|
|
276223
277050
|
exports.platformProbeCapability = platformProbeCapability;
|
|
277051
|
+
exports.poolMemoryThreshold = poolMemoryThreshold;
|
|
276224
277052
|
exports.powerMeterCapability = powerMeterCapability;
|
|
276225
277053
|
exports.prepareNotification = prepareNotification;
|
|
276226
277054
|
exports.presenceCapability = presenceCapability;
|
|
@@ -276242,6 +277070,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276242
277070
|
exports.recordingExportCapability = recordingExportCapability;
|
|
276243
277071
|
exports.rectsToCells = rectsToCells;
|
|
276244
277072
|
exports.requiresPython = requiresPython;
|
|
277073
|
+
exports.resetPoolBaseline = resetPoolBaseline;
|
|
276245
277074
|
exports.resolveAddonExecution = resolveAddonExecution;
|
|
276246
277075
|
exports.resolveAddonGroup = resolveAddonGroup;
|
|
276247
277076
|
exports.resolveAddonPlacement = resolveAddonPlacement;
|
|
@@ -276255,6 +277084,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276255
277084
|
exports.resolveHydratedFieldValue = require_sleep.resolveHydratedFieldValue;
|
|
276256
277085
|
exports.resolveModelFormat = resolveModelFormat;
|
|
276257
277086
|
exports.resolveMutate = resolveMutate;
|
|
277087
|
+
exports.resolvePoolMemoryPolicy = resolvePoolMemoryPolicy;
|
|
277088
|
+
exports.resolveRecordingProfiles = resolveRecordingProfiles;
|
|
276258
277089
|
exports.resolveRunnerId = resolveRunnerId;
|
|
276259
277090
|
exports.resolveScrubThumbnailGeometry = resolveScrubThumbnailGeometry;
|
|
276260
277091
|
exports.resolveVariantModelId = resolveVariantModelId;
|
|
@@ -276299,6 +277130,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276299
277130
|
exports.switchedOffIds = switchedOffIds;
|
|
276300
277131
|
exports.synthesizeSourceInfo = synthesizeSourceInfo;
|
|
276301
277132
|
exports.systemCapability = systemCapability;
|
|
277133
|
+
exports.systemEventFilterApplies = systemEventFilterApplies;
|
|
277134
|
+
exports.systemEventFilterAppliesToAnyKind = systemEventFilterAppliesToAnyKind;
|
|
276302
277135
|
exports.tamperCapability = tamperCapability;
|
|
276303
277136
|
exports.taskLogEntrySchema = taskLogEntrySchema;
|
|
276304
277137
|
exports.taskPhaseSchema = taskPhaseSchema;
|
|
@@ -276454,6 +277287,314 @@ var require_agent_config = __commonJS({
|
|
|
276454
277287
|
}
|
|
276455
277288
|
});
|
|
276456
277289
|
|
|
277290
|
+
// ../../server/backend/dist/core/server-update/system-ensure-prebuilds.js
|
|
277291
|
+
var require_system_ensure_prebuilds = __commonJS({
|
|
277292
|
+
"../../server/backend/dist/core/server-update/system-ensure-prebuilds.js"(exports) {
|
|
277293
|
+
"use strict";
|
|
277294
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
277295
|
+
exports.buildSystemEnsureNativePrebuilds = buildSystemEnsureNativePrebuilds;
|
|
277296
|
+
var system_1 = require_dist3();
|
|
277297
|
+
function buildSystemEnsureNativePrebuilds(logger) {
|
|
277298
|
+
return async (closureDir) => {
|
|
277299
|
+
await (0, system_1.ensureNativePrebuilds)(closureDir, { logger });
|
|
277300
|
+
};
|
|
277301
|
+
}
|
|
277302
|
+
}
|
|
277303
|
+
});
|
|
277304
|
+
|
|
277305
|
+
// ../../server/backend/dist/core/server-update/system-exec-npm.js
|
|
277306
|
+
var require_system_exec_npm = __commonJS({
|
|
277307
|
+
"../../server/backend/dist/core/server-update/system-exec-npm.js"(exports) {
|
|
277308
|
+
"use strict";
|
|
277309
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
277310
|
+
exports.buildSystemExecNpm = buildSystemExecNpm;
|
|
277311
|
+
var system_1 = require_dist3();
|
|
277312
|
+
function buildSystemExecNpm(options, runNpmFn = system_1.runNpm) {
|
|
277313
|
+
return async (args, opts) => {
|
|
277314
|
+
const { stdout } = await runNpmFn(args, {
|
|
277315
|
+
cacheDir: options.cacheDir,
|
|
277316
|
+
registry: options.registry,
|
|
277317
|
+
logger: options.logger,
|
|
277318
|
+
...opts.cwd !== void 0 ? { cwd: opts.cwd } : {},
|
|
277319
|
+
timeout: opts.timeoutMs
|
|
277320
|
+
});
|
|
277321
|
+
return { stdout };
|
|
277322
|
+
};
|
|
277323
|
+
}
|
|
277324
|
+
}
|
|
277325
|
+
});
|
|
277326
|
+
|
|
277327
|
+
// ../../server/backend/dist/core/update-availability-emitter.js
|
|
277328
|
+
var require_update_availability_emitter = __commonJS({
|
|
277329
|
+
"../../server/backend/dist/core/update-availability-emitter.js"(exports) {
|
|
277330
|
+
"use strict";
|
|
277331
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
277332
|
+
exports.UpdateAvailabilityEmitter = void 0;
|
|
277333
|
+
var types_1 = require_dist9();
|
|
277334
|
+
var UpdateAvailabilityEmitter = class {
|
|
277335
|
+
eventBus;
|
|
277336
|
+
source;
|
|
277337
|
+
store;
|
|
277338
|
+
latestByKey = /* @__PURE__ */ new Map();
|
|
277339
|
+
held = /* @__PURE__ */ new Map();
|
|
277340
|
+
/** Set when in-memory state diverged from what the store last saw. */
|
|
277341
|
+
dirty = false;
|
|
277342
|
+
constructor(eventBus, source, store) {
|
|
277343
|
+
this.eventBus = eventBus;
|
|
277344
|
+
this.source = source;
|
|
277345
|
+
this.store = store;
|
|
277346
|
+
const persisted = store?.load() ?? null;
|
|
277347
|
+
if (persisted === null)
|
|
277348
|
+
return;
|
|
277349
|
+
for (const [key, version] of Object.entries(persisted.latestByKey)) {
|
|
277350
|
+
this.latestByKey.set(key, version);
|
|
277351
|
+
}
|
|
277352
|
+
for (const [key, candidate] of Object.entries(persisted.held)) {
|
|
277353
|
+
this.held.set(key, candidate);
|
|
277354
|
+
}
|
|
277355
|
+
}
|
|
277356
|
+
publishSnapshot(target, candidates, nodeId) {
|
|
277357
|
+
const scope = nodeId ?? "hub";
|
|
277358
|
+
const present = /* @__PURE__ */ new Set();
|
|
277359
|
+
let latestChanged = false;
|
|
277360
|
+
for (const candidate of candidates) {
|
|
277361
|
+
if (candidate.target !== target)
|
|
277362
|
+
continue;
|
|
277363
|
+
const stamped = stampNode(candidate, nodeId);
|
|
277364
|
+
const key = this.key(stamped);
|
|
277365
|
+
present.add(key);
|
|
277366
|
+
if (this.note(stamped))
|
|
277367
|
+
latestChanged = true;
|
|
277368
|
+
}
|
|
277369
|
+
for (const key of [...this.held.keys()]) {
|
|
277370
|
+
if (!key.startsWith(`${target}:${scope}:`))
|
|
277371
|
+
continue;
|
|
277372
|
+
if (present.has(key))
|
|
277373
|
+
continue;
|
|
277374
|
+
this.held.delete(key);
|
|
277375
|
+
this.latestByKey.delete(key);
|
|
277376
|
+
this.dirty = true;
|
|
277377
|
+
}
|
|
277378
|
+
this.persistIfDirty();
|
|
277379
|
+
if (!latestChanged)
|
|
277380
|
+
return;
|
|
277381
|
+
this.emitList(target);
|
|
277382
|
+
}
|
|
277383
|
+
/** Publish a partial candidate set without treating omissions as up-to-date. */
|
|
277384
|
+
publishCandidates(candidates) {
|
|
277385
|
+
let latestChanged = false;
|
|
277386
|
+
const targets = /* @__PURE__ */ new Set();
|
|
277387
|
+
for (const candidate of candidates) {
|
|
277388
|
+
targets.add(candidate.target);
|
|
277389
|
+
if (this.note(candidate))
|
|
277390
|
+
latestChanged = true;
|
|
277391
|
+
}
|
|
277392
|
+
this.persistIfDirty();
|
|
277393
|
+
if (!latestChanged)
|
|
277394
|
+
return;
|
|
277395
|
+
for (const target of targets)
|
|
277396
|
+
this.emitList(target);
|
|
277397
|
+
}
|
|
277398
|
+
note(candidate) {
|
|
277399
|
+
const key = this.key(candidate);
|
|
277400
|
+
const previous = this.latestByKey.get(key);
|
|
277401
|
+
const previousHeld = this.held.get(key);
|
|
277402
|
+
if (previousHeld === void 0 || previousHeld.currentVersion !== candidate.currentVersion || previousHeld.latestVersion !== candidate.latestVersion || previousHeld.nodeId !== candidate.nodeId) {
|
|
277403
|
+
this.dirty = true;
|
|
277404
|
+
}
|
|
277405
|
+
this.held.set(key, candidate);
|
|
277406
|
+
this.latestByKey.set(key, candidate.latestVersion);
|
|
277407
|
+
return previous !== candidate.latestVersion;
|
|
277408
|
+
}
|
|
277409
|
+
/**
|
|
277410
|
+
* Flush to the durable store. Called BEFORE the emit so a crash between the
|
|
277411
|
+
* two costs a duplicate announcement, never a silent one.
|
|
277412
|
+
*/
|
|
277413
|
+
persistIfDirty() {
|
|
277414
|
+
if (!this.dirty)
|
|
277415
|
+
return;
|
|
277416
|
+
this.dirty = false;
|
|
277417
|
+
this.store?.save({
|
|
277418
|
+
latestByKey: Object.fromEntries(this.latestByKey),
|
|
277419
|
+
held: Object.fromEntries(this.held)
|
|
277420
|
+
});
|
|
277421
|
+
}
|
|
277422
|
+
emitList(target) {
|
|
277423
|
+
const list = [...this.held.values()].filter((candidate) => candidate.target === target);
|
|
277424
|
+
if (list.length === 0)
|
|
277425
|
+
return;
|
|
277426
|
+
const packages = list.map((candidate) => ({
|
|
277427
|
+
packageName: candidate.packageName,
|
|
277428
|
+
currentVersion: candidate.currentVersion,
|
|
277429
|
+
latestVersion: candidate.latestVersion,
|
|
277430
|
+
...candidate.nodeId !== void 0 ? { nodeId: candidate.nodeId } : {}
|
|
277431
|
+
}));
|
|
277432
|
+
const nodeIds = unique(packages.map((pkg) => pkg.nodeId).filter((id) => id !== void 0));
|
|
277433
|
+
const head = list[0];
|
|
277434
|
+
this.eventBus.emit({
|
|
277435
|
+
id: `update.available:${target}:${packages.map((pkg) => `${pkg.nodeId ?? "hub"}:${pkg.packageName}@${pkg.latestVersion}`).toSorted().join(",")}`,
|
|
277436
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
277437
|
+
source: this.source,
|
|
277438
|
+
category: types_1.EventCategory.UpdateAvailable,
|
|
277439
|
+
data: {
|
|
277440
|
+
target,
|
|
277441
|
+
packageName: head.packageName,
|
|
277442
|
+
currentVersion: head.currentVersion,
|
|
277443
|
+
latestVersion: head.latestVersion,
|
|
277444
|
+
packages,
|
|
277445
|
+
...head.nodeId !== void 0 ? { nodeId: head.nodeId } : {},
|
|
277446
|
+
...nodeIds.length > 0 ? { nodeIds } : {}
|
|
277447
|
+
}
|
|
277448
|
+
});
|
|
277449
|
+
}
|
|
277450
|
+
key(candidate) {
|
|
277451
|
+
return `${candidate.target}:${candidate.nodeId ?? "hub"}:${candidate.packageName}`;
|
|
277452
|
+
}
|
|
277453
|
+
};
|
|
277454
|
+
exports.UpdateAvailabilityEmitter = UpdateAvailabilityEmitter;
|
|
277455
|
+
function stampNode(candidate, nodeId) {
|
|
277456
|
+
if (nodeId === void 0 || candidate.nodeId !== void 0)
|
|
277457
|
+
return candidate;
|
|
277458
|
+
return { ...candidate, nodeId };
|
|
277459
|
+
}
|
|
277460
|
+
function unique(values) {
|
|
277461
|
+
return [...new Set(values)];
|
|
277462
|
+
}
|
|
277463
|
+
}
|
|
277464
|
+
});
|
|
277465
|
+
|
|
277466
|
+
// ../../server/backend/dist/core/update-availability-store.js
|
|
277467
|
+
var require_update_availability_store = __commonJS({
|
|
277468
|
+
"../../server/backend/dist/core/update-availability-store.js"(exports) {
|
|
277469
|
+
"use strict";
|
|
277470
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
277471
|
+
if (k2 === void 0) k2 = k;
|
|
277472
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
277473
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
277474
|
+
desc = { enumerable: true, get: function() {
|
|
277475
|
+
return m[k];
|
|
277476
|
+
} };
|
|
277477
|
+
}
|
|
277478
|
+
Object.defineProperty(o, k2, desc);
|
|
277479
|
+
}) : (function(o, m, k, k2) {
|
|
277480
|
+
if (k2 === void 0) k2 = k;
|
|
277481
|
+
o[k2] = m[k];
|
|
277482
|
+
}));
|
|
277483
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
277484
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
277485
|
+
}) : function(o, v) {
|
|
277486
|
+
o["default"] = v;
|
|
277487
|
+
});
|
|
277488
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() {
|
|
277489
|
+
var ownKeys = function(o) {
|
|
277490
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
277491
|
+
var ar = [];
|
|
277492
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
277493
|
+
return ar;
|
|
277494
|
+
};
|
|
277495
|
+
return ownKeys(o);
|
|
277496
|
+
};
|
|
277497
|
+
return function(mod) {
|
|
277498
|
+
if (mod && mod.__esModule) return mod;
|
|
277499
|
+
var result = {};
|
|
277500
|
+
if (mod != null) {
|
|
277501
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
277502
|
+
}
|
|
277503
|
+
__setModuleDefault(result, mod);
|
|
277504
|
+
return result;
|
|
277505
|
+
};
|
|
277506
|
+
})();
|
|
277507
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
277508
|
+
exports.FileUpdateAvailabilityStore = exports.UPDATE_AVAILABILITY_DIR = void 0;
|
|
277509
|
+
var fs = __importStar(__require("fs"));
|
|
277510
|
+
var path = __importStar(__require("path"));
|
|
277511
|
+
var types_1 = require_dist9();
|
|
277512
|
+
exports.UPDATE_AVAILABILITY_DIR = "update-availability";
|
|
277513
|
+
var FileUpdateAvailabilityStore = class {
|
|
277514
|
+
scopeId;
|
|
277515
|
+
logger;
|
|
277516
|
+
filePath;
|
|
277517
|
+
constructor(dataDir, scopeId, logger) {
|
|
277518
|
+
this.scopeId = scopeId;
|
|
277519
|
+
this.logger = logger;
|
|
277520
|
+
this.filePath = path.join(dataDir, exports.UPDATE_AVAILABILITY_DIR, `${scopeId}.json`);
|
|
277521
|
+
}
|
|
277522
|
+
load() {
|
|
277523
|
+
try {
|
|
277524
|
+
if (!fs.existsSync(this.filePath))
|
|
277525
|
+
return null;
|
|
277526
|
+
const parsed = JSON.parse(fs.readFileSync(this.filePath, "utf-8"));
|
|
277527
|
+
return parseState(parsed);
|
|
277528
|
+
} catch (error) {
|
|
277529
|
+
this.logger.warn("Update-availability state unreadable; starting from empty", {
|
|
277530
|
+
meta: { scopeId: this.scopeId, path: this.filePath, error: (0, types_1.errMsg)(error) }
|
|
277531
|
+
});
|
|
277532
|
+
return null;
|
|
277533
|
+
}
|
|
277534
|
+
}
|
|
277535
|
+
save(state) {
|
|
277536
|
+
try {
|
|
277537
|
+
fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
|
277538
|
+
const tmp = `${this.filePath}.tmp`;
|
|
277539
|
+
fs.writeFileSync(tmp, JSON.stringify(state, null, 2));
|
|
277540
|
+
fs.renameSync(tmp, this.filePath);
|
|
277541
|
+
} catch (error) {
|
|
277542
|
+
this.logger.warn("Failed to persist update-availability state", {
|
|
277543
|
+
meta: { scopeId: this.scopeId, path: this.filePath, error: (0, types_1.errMsg)(error) }
|
|
277544
|
+
});
|
|
277545
|
+
}
|
|
277546
|
+
}
|
|
277547
|
+
};
|
|
277548
|
+
exports.FileUpdateAvailabilityStore = FileUpdateAvailabilityStore;
|
|
277549
|
+
function parseState(raw) {
|
|
277550
|
+
if (raw === null || typeof raw !== "object")
|
|
277551
|
+
return null;
|
|
277552
|
+
const latestRaw = Reflect.get(raw, "latestByKey");
|
|
277553
|
+
const heldRaw = Reflect.get(raw, "held");
|
|
277554
|
+
if (latestRaw === null || typeof latestRaw !== "object")
|
|
277555
|
+
return null;
|
|
277556
|
+
if (heldRaw === null || typeof heldRaw !== "object")
|
|
277557
|
+
return null;
|
|
277558
|
+
const latestByKey = {};
|
|
277559
|
+
for (const [key, value] of Object.entries(latestRaw)) {
|
|
277560
|
+
if (typeof value === "string")
|
|
277561
|
+
latestByKey[key] = value;
|
|
277562
|
+
}
|
|
277563
|
+
const held = {};
|
|
277564
|
+
for (const [key, value] of Object.entries(heldRaw)) {
|
|
277565
|
+
const candidate = parseCandidate(value);
|
|
277566
|
+
if (candidate !== null)
|
|
277567
|
+
held[key] = candidate;
|
|
277568
|
+
}
|
|
277569
|
+
return { latestByKey, held };
|
|
277570
|
+
}
|
|
277571
|
+
function parseCandidate(raw) {
|
|
277572
|
+
if (raw === null || typeof raw !== "object")
|
|
277573
|
+
return null;
|
|
277574
|
+
const target = Reflect.get(raw, "target");
|
|
277575
|
+
const packageName = Reflect.get(raw, "packageName");
|
|
277576
|
+
const currentVersion = Reflect.get(raw, "currentVersion");
|
|
277577
|
+
const latestVersion = Reflect.get(raw, "latestVersion");
|
|
277578
|
+
const nodeId = Reflect.get(raw, "nodeId");
|
|
277579
|
+
if (target !== "addon" && target !== "server")
|
|
277580
|
+
return null;
|
|
277581
|
+
if (typeof packageName !== "string")
|
|
277582
|
+
return null;
|
|
277583
|
+
if (typeof currentVersion !== "string")
|
|
277584
|
+
return null;
|
|
277585
|
+
if (typeof latestVersion !== "string")
|
|
277586
|
+
return null;
|
|
277587
|
+
return {
|
|
277588
|
+
target,
|
|
277589
|
+
packageName,
|
|
277590
|
+
currentVersion,
|
|
277591
|
+
latestVersion,
|
|
277592
|
+
...typeof nodeId === "string" ? { nodeId } : {}
|
|
277593
|
+
};
|
|
277594
|
+
}
|
|
277595
|
+
}
|
|
277596
|
+
});
|
|
277597
|
+
|
|
276457
277598
|
// ../../server/backend/dist/server-root/index.js
|
|
276458
277599
|
var require_server_root = __commonJS({
|
|
276459
277600
|
"../../server/backend/dist/server-root/index.js"(exports, module) {
|
|
@@ -277958,43 +279099,6 @@ var require_server_root = __commonJS({
|
|
|
277958
279099
|
}
|
|
277959
279100
|
});
|
|
277960
279101
|
|
|
277961
|
-
// ../../server/backend/dist/core/server-update/system-exec-npm.js
|
|
277962
|
-
var require_system_exec_npm = __commonJS({
|
|
277963
|
-
"../../server/backend/dist/core/server-update/system-exec-npm.js"(exports) {
|
|
277964
|
-
"use strict";
|
|
277965
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
277966
|
-
exports.buildSystemExecNpm = buildSystemExecNpm;
|
|
277967
|
-
var system_1 = require_dist3();
|
|
277968
|
-
function buildSystemExecNpm(options, runNpmFn = system_1.runNpm) {
|
|
277969
|
-
return async (args, opts) => {
|
|
277970
|
-
const { stdout } = await runNpmFn(args, {
|
|
277971
|
-
cacheDir: options.cacheDir,
|
|
277972
|
-
registry: options.registry,
|
|
277973
|
-
logger: options.logger,
|
|
277974
|
-
...opts.cwd !== void 0 ? { cwd: opts.cwd } : {},
|
|
277975
|
-
timeout: opts.timeoutMs
|
|
277976
|
-
});
|
|
277977
|
-
return { stdout };
|
|
277978
|
-
};
|
|
277979
|
-
}
|
|
277980
|
-
}
|
|
277981
|
-
});
|
|
277982
|
-
|
|
277983
|
-
// ../../server/backend/dist/core/server-update/system-ensure-prebuilds.js
|
|
277984
|
-
var require_system_ensure_prebuilds = __commonJS({
|
|
277985
|
-
"../../server/backend/dist/core/server-update/system-ensure-prebuilds.js"(exports) {
|
|
277986
|
-
"use strict";
|
|
277987
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
277988
|
-
exports.buildSystemEnsureNativePrebuilds = buildSystemEnsureNativePrebuilds;
|
|
277989
|
-
var system_1 = require_dist3();
|
|
277990
|
-
function buildSystemEnsureNativePrebuilds(logger) {
|
|
277991
|
-
return async (closureDir) => {
|
|
277992
|
-
await (0, system_1.ensureNativePrebuilds)(closureDir, { logger });
|
|
277993
|
-
};
|
|
277994
|
-
}
|
|
277995
|
-
}
|
|
277996
|
-
});
|
|
277997
|
-
|
|
277998
279102
|
// ../../server/backend/dist/agent/agent-update-service.js
|
|
277999
279103
|
var require_agent_update_service = __commonJS({
|
|
278000
279104
|
"../../server/backend/dist/agent/agent-update-service.js"(exports) {
|
|
@@ -278042,11 +279146,16 @@ var require_agent_update_service = __commonJS({
|
|
|
278042
279146
|
exports.resolveAgentPackageJsonPath = resolveAgentPackageJsonPath;
|
|
278043
279147
|
exports.scheduleAgentRestart = scheduleAgentRestart;
|
|
278044
279148
|
exports.agentRuntimeManifestEntry = agentRuntimeManifestEntry;
|
|
279149
|
+
var node_crypto_1 = __require("crypto");
|
|
278045
279150
|
var fs = __importStar(__require("fs"));
|
|
278046
279151
|
var path = __importStar(__require("path"));
|
|
278047
|
-
var
|
|
278048
|
-
var
|
|
279152
|
+
var system_1 = require_dist3();
|
|
279153
|
+
var types_1 = require_dist9();
|
|
278049
279154
|
var system_ensure_prebuilds_js_1 = require_system_ensure_prebuilds();
|
|
279155
|
+
var system_exec_npm_js_1 = require_system_exec_npm();
|
|
279156
|
+
var update_availability_emitter_js_1 = require_update_availability_emitter();
|
|
279157
|
+
var update_availability_store_js_1 = require_update_availability_store();
|
|
279158
|
+
var index_js_1 = require_server_root();
|
|
278050
279159
|
exports.AGENT_RUNTIME_ADDON_ID = "agent-runtime";
|
|
278051
279160
|
exports.AGENT_RESTART_GRACE_MS = 2e3;
|
|
278052
279161
|
function isRunningInContainer(existsSyncFn) {
|
|
@@ -278091,7 +279200,14 @@ var require_agent_update_service = __commonJS({
|
|
|
278091
279200
|
grace.unref();
|
|
278092
279201
|
}
|
|
278093
279202
|
var AgentUpdateService = class extends index_js_1.RootUpdateService {
|
|
279203
|
+
updateAvailability;
|
|
279204
|
+
eventBus;
|
|
279205
|
+
nodeId;
|
|
279206
|
+
agentDataDir;
|
|
279207
|
+
/** The base class keeps `logger` private; hold our own for the store. */
|
|
279208
|
+
agentLogger;
|
|
278094
279209
|
constructor(options) {
|
|
279210
|
+
const restartAgent = options.restartAgent ?? ((requestedBy) => scheduleAgentRestart(options.logger, requestedBy, options.dataDir));
|
|
278095
279211
|
super({
|
|
278096
279212
|
spec: index_js_1.HUB_ROOT_SPEC,
|
|
278097
279213
|
// The agent's on-disk root IS `@camstack/server` (booted via
|
|
@@ -278103,7 +279219,10 @@ var require_agent_update_service = __commonJS({
|
|
|
278103
279219
|
seedDir: "CAMSTACK_SEED_SERVER_DIR"
|
|
278104
279220
|
},
|
|
278105
279221
|
logger: options.logger,
|
|
278106
|
-
restartServer:
|
|
279222
|
+
restartServer: (requestedBy) => {
|
|
279223
|
+
writeAgentRestartMarker(options.dataDir, requestedBy);
|
|
279224
|
+
restartAgent(requestedBy);
|
|
279225
|
+
},
|
|
278107
279226
|
dataDir: options.dataDir,
|
|
278108
279227
|
runningPackageJsonPath: options.runningPackageJsonPath ?? resolveAgentPackageJsonPath(__dirname),
|
|
278109
279228
|
workspaceProbeDir: __dirname,
|
|
@@ -278121,9 +279240,79 @@ var require_agent_update_service = __commonJS({
|
|
|
278121
279240
|
env: options.env,
|
|
278122
279241
|
now: options.now
|
|
278123
279242
|
});
|
|
279243
|
+
this.nodeId = options.nodeId ?? process.env["CAMSTACK_NODE_ID"] ?? "agent";
|
|
279244
|
+
this.agentDataDir = options.dataDir;
|
|
279245
|
+
this.agentLogger = options.logger;
|
|
279246
|
+
this.eventBus = options.eventBus ?? null;
|
|
279247
|
+
this.updateAvailability = options.eventBus !== void 0 ? new update_availability_emitter_js_1.UpdateAvailabilityEmitter(options.eventBus, { type: "core", id: "agent-update-service" }, options.updateAvailabilityStore) : null;
|
|
279248
|
+
}
|
|
279249
|
+
/**
|
|
279250
|
+
* Bind the agent's real event bus once the mesh is up. The dedup state is
|
|
279251
|
+
* persisted under the agent's data dir from here on — an agent restarts on
|
|
279252
|
+
* every root update, which is exactly when its availability list is longest.
|
|
279253
|
+
*/
|
|
279254
|
+
bindAvailability(eventBus, nodeId) {
|
|
279255
|
+
this.eventBus = eventBus;
|
|
279256
|
+
this.nodeId = nodeId;
|
|
279257
|
+
this.updateAvailability = new update_availability_emitter_js_1.UpdateAvailabilityEmitter(eventBus, { type: "core", id: "agent-update-service" }, new update_availability_store_js_1.FileUpdateAvailabilityStore(this.agentDataDir, "agent-update", this.agentLogger));
|
|
279258
|
+
}
|
|
279259
|
+
async checkServerUpdate() {
|
|
279260
|
+
const result = await super.checkServerUpdate();
|
|
279261
|
+
if (result.error !== null)
|
|
279262
|
+
return result;
|
|
279263
|
+
this.updateAvailability?.publishSnapshot("server", result.updateAvailable && result.runningVersion !== null && result.latestVersion !== null ? [
|
|
279264
|
+
{
|
|
279265
|
+
target: "server",
|
|
279266
|
+
packageName: result.packageName,
|
|
279267
|
+
currentVersion: result.runningVersion,
|
|
279268
|
+
latestVersion: result.latestVersion,
|
|
279269
|
+
nodeId: this.nodeId
|
|
279270
|
+
}
|
|
279271
|
+
] : [], this.nodeId);
|
|
279272
|
+
return result;
|
|
279273
|
+
}
|
|
279274
|
+
/** Consume `.restart-pending` and emit `system.restart-completed` if present. */
|
|
279275
|
+
emitRestartCompletedIfPending() {
|
|
279276
|
+
const bus = this.eventBus;
|
|
279277
|
+
if (bus === null)
|
|
279278
|
+
return;
|
|
279279
|
+
const marker = (0, system_1.readPendingRestart)(this.agentDataDir);
|
|
279280
|
+
if (marker === null)
|
|
279281
|
+
return;
|
|
279282
|
+
const payload = {
|
|
279283
|
+
kind: marker.kind,
|
|
279284
|
+
requestedAt: marker.requestedAt,
|
|
279285
|
+
nodeId: this.nodeId,
|
|
279286
|
+
...marker.packageName !== void 0 ? { packageName: marker.packageName } : {},
|
|
279287
|
+
...marker.fromVersion !== void 0 ? { fromVersion: marker.fromVersion } : {},
|
|
279288
|
+
...marker.toVersion !== void 0 ? { toVersion: marker.toVersion } : {},
|
|
279289
|
+
...marker.requestedBy !== void 0 ? { requestedBy: marker.requestedBy } : {}
|
|
279290
|
+
};
|
|
279291
|
+
bus.emit({
|
|
279292
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
279293
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
279294
|
+
source: { type: "core", id: "agent-update-service" },
|
|
279295
|
+
category: types_1.EventCategory.SystemRestartCompleted,
|
|
279296
|
+
data: payload
|
|
279297
|
+
});
|
|
278124
279298
|
}
|
|
278125
279299
|
};
|
|
278126
279300
|
exports.AgentUpdateService = AgentUpdateService;
|
|
279301
|
+
function writeAgentRestartMarker(dataDir, requestedBy) {
|
|
279302
|
+
const toVersion = /@([^@\s]+)$/.exec(requestedBy)?.[1];
|
|
279303
|
+
try {
|
|
279304
|
+
(0, system_1.writePendingRestart)(dataDir, {
|
|
279305
|
+
kind: requestedBy.startsWith("server-update:") ? "framework-update" : "manual",
|
|
279306
|
+
requestedAt: Date.now(),
|
|
279307
|
+
requestedBy,
|
|
279308
|
+
...requestedBy.startsWith("server-update:") ? {
|
|
279309
|
+
packageName: "@camstack/server",
|
|
279310
|
+
...toVersion !== void 0 ? { toVersion } : {}
|
|
279311
|
+
} : {}
|
|
279312
|
+
});
|
|
279313
|
+
} catch {
|
|
279314
|
+
}
|
|
279315
|
+
}
|
|
278127
279316
|
function agentRuntimeManifestEntry() {
|
|
278128
279317
|
return { addonId: exports.AGENT_RUNTIME_ADDON_ID, capabilities: ["server-management"] };
|
|
278129
279318
|
}
|
|
@@ -370765,6 +371954,7 @@ var require_main2 = __commonJS({
|
|
|
370765
371954
|
agentBootConfirmed = true;
|
|
370766
371955
|
try {
|
|
370767
371956
|
agentUpdateService.confirmBootHealthy();
|
|
371957
|
+
agentUpdateService.emitRestartCompletedIfPending();
|
|
370768
371958
|
} catch (err) {
|
|
370769
371959
|
consoleLogger.warn(`agent root boot confirmation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
370770
371960
|
}
|
|
@@ -370944,9 +372134,10 @@ var require_main2 = __commonJS({
|
|
|
370944
372134
|
return;
|
|
370945
372135
|
reconcileHubUrlFromRegistry();
|
|
370946
372136
|
});
|
|
372137
|
+
const agentBrokerEventBus = (0, system_1.getBrokerEventBus)(broker);
|
|
372138
|
+
agentUpdateService.bindAvailability(agentBrokerEventBus, broker.nodeID);
|
|
370947
372139
|
let udsEventBridgeDispose = null;
|
|
370948
372140
|
if (agentUdsRegistry !== void 0) {
|
|
370949
|
-
const agentBrokerEventBus = (0, system_1.getBrokerEventBus)(broker);
|
|
370950
372141
|
udsEventBridgeDispose = (0, system_1.createUdsEventBridge)({
|
|
370951
372142
|
registry: agentUdsRegistry,
|
|
370952
372143
|
parentBus: agentBrokerEventBus,
|
|
@@ -386589,61 +387780,126 @@ var require_ws3 = __commonJS({
|
|
|
386589
387780
|
}
|
|
386590
387781
|
});
|
|
386591
387782
|
|
|
386592
|
-
// ../../server/backend/dist/core/update-
|
|
386593
|
-
var
|
|
386594
|
-
"../../server/backend/dist/core/update-
|
|
387783
|
+
// ../../server/backend/dist/core/updates/update-check-scheduler.js
|
|
387784
|
+
var require_update_check_scheduler = __commonJS({
|
|
387785
|
+
"../../server/backend/dist/core/updates/update-check-scheduler.js"(exports) {
|
|
386595
387786
|
"use strict";
|
|
386596
387787
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
386597
|
-
exports.
|
|
387788
|
+
exports.UpdateCheckScheduler = exports.MAX_UPDATE_CHECK_INTERVAL_SECONDS = exports.MIN_UPDATE_CHECK_INTERVAL_SECONDS = exports.DEFAULT_UPDATE_CHECK_INTERVAL_SECONDS = void 0;
|
|
387789
|
+
exports.clampUpdateCheckInterval = clampUpdateCheckInterval;
|
|
386598
387790
|
var types_1 = require_dist9();
|
|
386599
|
-
|
|
386600
|
-
|
|
386601
|
-
|
|
386602
|
-
|
|
386603
|
-
|
|
386604
|
-
|
|
386605
|
-
|
|
387791
|
+
exports.DEFAULT_UPDATE_CHECK_INTERVAL_SECONDS = 6 * 60 * 60;
|
|
387792
|
+
exports.MIN_UPDATE_CHECK_INTERVAL_SECONDS = 15 * 60;
|
|
387793
|
+
exports.MAX_UPDATE_CHECK_INTERVAL_SECONDS = 7 * 24 * 60 * 60;
|
|
387794
|
+
var DEFAULT_INITIAL_DELAY_MS = 9e4;
|
|
387795
|
+
function clampUpdateCheckInterval(seconds) {
|
|
387796
|
+
if (!Number.isFinite(seconds))
|
|
387797
|
+
return exports.DEFAULT_UPDATE_CHECK_INTERVAL_SECONDS;
|
|
387798
|
+
const whole = Math.floor(seconds);
|
|
387799
|
+
if (whole < exports.MIN_UPDATE_CHECK_INTERVAL_SECONDS)
|
|
387800
|
+
return exports.MIN_UPDATE_CHECK_INTERVAL_SECONDS;
|
|
387801
|
+
if (whole > exports.MAX_UPDATE_CHECK_INTERVAL_SECONDS)
|
|
387802
|
+
return exports.MAX_UPDATE_CHECK_INTERVAL_SECONDS;
|
|
387803
|
+
return whole;
|
|
387804
|
+
}
|
|
387805
|
+
var UpdateCheckScheduler = class {
|
|
387806
|
+
logger;
|
|
387807
|
+
targets;
|
|
387808
|
+
getIntervalSeconds;
|
|
387809
|
+
initialDelayMs;
|
|
387810
|
+
timer = null;
|
|
387811
|
+
bootTimer = null;
|
|
387812
|
+
intervalSeconds = exports.DEFAULT_UPDATE_CHECK_INTERVAL_SECONDS;
|
|
387813
|
+
sweeping = false;
|
|
387814
|
+
constructor(options) {
|
|
387815
|
+
this.logger = options.logger;
|
|
387816
|
+
this.targets = options.targets;
|
|
387817
|
+
this.getIntervalSeconds = options.getIntervalSeconds;
|
|
387818
|
+
this.initialDelayMs = options.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;
|
|
386606
387819
|
}
|
|
386607
|
-
|
|
386608
|
-
|
|
386609
|
-
|
|
386610
|
-
|
|
386611
|
-
|
|
386612
|
-
|
|
386613
|
-
|
|
386614
|
-
|
|
386615
|
-
this.
|
|
387820
|
+
/** Interval currently armed, after clamping. Exposed for assertions + logs. */
|
|
387821
|
+
currentIntervalSeconds() {
|
|
387822
|
+
return this.intervalSeconds;
|
|
387823
|
+
}
|
|
387824
|
+
/** Arm the poller. Safe to call twice — the previous timers are replaced. */
|
|
387825
|
+
start() {
|
|
387826
|
+
this.arm();
|
|
387827
|
+
if (this.bootTimer !== null)
|
|
387828
|
+
clearTimeout(this.bootTimer);
|
|
387829
|
+
this.bootTimer = setTimeout(() => {
|
|
387830
|
+
this.bootTimer = null;
|
|
387831
|
+
void this.runSweep();
|
|
387832
|
+
}, this.initialDelayMs);
|
|
387833
|
+
this.bootTimer.unref?.();
|
|
387834
|
+
}
|
|
387835
|
+
/** Re-read the interval from settings and re-arm if it moved. */
|
|
387836
|
+
reschedule() {
|
|
387837
|
+
const next = clampUpdateCheckInterval(this.getIntervalSeconds());
|
|
387838
|
+
if (this.timer !== null && next === this.intervalSeconds)
|
|
387839
|
+
return;
|
|
387840
|
+
this.arm();
|
|
387841
|
+
}
|
|
387842
|
+
stop() {
|
|
387843
|
+
if (this.timer !== null)
|
|
387844
|
+
clearInterval(this.timer);
|
|
387845
|
+
if (this.bootTimer !== null)
|
|
387846
|
+
clearTimeout(this.bootTimer);
|
|
387847
|
+
this.timer = null;
|
|
387848
|
+
this.bootTimer = null;
|
|
387849
|
+
}
|
|
387850
|
+
/**
|
|
387851
|
+
* One full sweep. Never throws and never runs concurrently with itself — an
|
|
387852
|
+
* overlapping tick (slow registry, long agent fan-out) is dropped WITH a log
|
|
387853
|
+
* rather than doubling the npm traffic.
|
|
387854
|
+
*/
|
|
387855
|
+
async runSweep() {
|
|
387856
|
+
if (this.sweeping) {
|
|
387857
|
+
this.logger.warn("Update check skipped \u2014 previous sweep still running");
|
|
387858
|
+
return;
|
|
386616
387859
|
}
|
|
386617
|
-
|
|
386618
|
-
|
|
386619
|
-
|
|
387860
|
+
this.sweeping = true;
|
|
387861
|
+
try {
|
|
387862
|
+
const nodes = this.targets.listNodes();
|
|
387863
|
+
await this.attempt("hub addon packages", "hub", () => this.targets.checkHubAddons());
|
|
387864
|
+
await this.attempt("framework packages", "hub", () => this.targets.checkFrameworkPackages());
|
|
387865
|
+
for (const node of nodes) {
|
|
387866
|
+
if (!node.isOnline) {
|
|
387867
|
+
this.logger.warn("Update check skipped \u2014 node offline", { tags: { nodeId: node.id } });
|
|
387868
|
+
continue;
|
|
387869
|
+
}
|
|
387870
|
+
await this.attempt("node server package", node.id, () => this.targets.checkNodeServerUpdate(node.id, node.isHub));
|
|
387871
|
+
if (node.isHub)
|
|
387872
|
+
continue;
|
|
387873
|
+
await this.attempt("agent addon packages", node.id, () => this.targets.checkAgentAddons(node.id));
|
|
386620
387874
|
}
|
|
387875
|
+
} finally {
|
|
387876
|
+
this.sweeping = false;
|
|
386621
387877
|
}
|
|
386622
387878
|
}
|
|
386623
|
-
|
|
386624
|
-
|
|
386625
|
-
|
|
386626
|
-
|
|
386627
|
-
|
|
386628
|
-
|
|
386629
|
-
const key = this.key(candidate);
|
|
386630
|
-
const signature = `${candidate.currentVersion}->${candidate.latestVersion}`;
|
|
386631
|
-
if (this.signatures.get(key) === signature)
|
|
386632
|
-
return;
|
|
386633
|
-
this.signatures.set(key, signature);
|
|
386634
|
-
this.eventBus.emit({
|
|
386635
|
-
id: `update.available:${key}:${signature}`,
|
|
386636
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
386637
|
-
source: this.source,
|
|
386638
|
-
category: types_1.EventCategory.UpdateAvailable,
|
|
386639
|
-
data: candidate
|
|
387879
|
+
arm() {
|
|
387880
|
+
if (this.timer !== null)
|
|
387881
|
+
clearInterval(this.timer);
|
|
387882
|
+
this.intervalSeconds = clampUpdateCheckInterval(this.getIntervalSeconds());
|
|
387883
|
+
this.logger.info("Update check scheduled", {
|
|
387884
|
+
meta: { intervalSeconds: this.intervalSeconds }
|
|
386640
387885
|
});
|
|
387886
|
+
this.timer = setInterval(() => {
|
|
387887
|
+
void this.runSweep();
|
|
387888
|
+
}, this.intervalSeconds * 1e3);
|
|
387889
|
+
this.timer.unref?.();
|
|
386641
387890
|
}
|
|
386642
|
-
|
|
386643
|
-
|
|
387891
|
+
async attempt(what, nodeId, run) {
|
|
387892
|
+
try {
|
|
387893
|
+
await run();
|
|
387894
|
+
} catch (error) {
|
|
387895
|
+
this.logger.warn(`Update check failed \u2014 ${what}`, {
|
|
387896
|
+
tags: { nodeId },
|
|
387897
|
+
meta: { error: (0, types_1.errMsg)(error) }
|
|
387898
|
+
});
|
|
387899
|
+
}
|
|
386644
387900
|
}
|
|
386645
387901
|
};
|
|
386646
|
-
exports.
|
|
387902
|
+
exports.UpdateCheckScheduler = UpdateCheckScheduler;
|
|
386647
387903
|
}
|
|
386648
387904
|
});
|
|
386649
387905
|
|
|
@@ -386922,8 +388178,9 @@ var require_addon_package_service = __commonJS({
|
|
|
386922
388178
|
var node_util_1 = __require("util");
|
|
386923
388179
|
var system_1 = require_dist3();
|
|
386924
388180
|
var types_1 = require_dist9();
|
|
386925
|
-
var package_dir_utils_js_1 = require_package_dir_utils();
|
|
386926
388181
|
var update_availability_emitter_js_1 = require_update_availability_emitter();
|
|
388182
|
+
var update_check_scheduler_js_1 = require_update_check_scheduler();
|
|
388183
|
+
var package_dir_utils_js_1 = require_package_dir_utils();
|
|
386927
388184
|
var execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
386928
388185
|
exports.SYSTEM_PACKAGE = "@camstack/system";
|
|
386929
388186
|
exports.AUTO_UPDATE_EXCLUDED_PACKAGES = /* @__PURE__ */ new Set([
|
|
@@ -387016,10 +388273,20 @@ var require_addon_package_service = __commonJS({
|
|
|
387016
388273
|
updateAvailability;
|
|
387017
388274
|
// -- Auto-update state ----------------------------------------------------
|
|
387018
388275
|
autoUpdateConfig = {
|
|
387019
|
-
global: {
|
|
388276
|
+
global: {
|
|
388277
|
+
channel: "off",
|
|
388278
|
+
intervalSeconds: 21600,
|
|
388279
|
+
updateCheckIntervalSeconds: update_check_scheduler_js_1.DEFAULT_UPDATE_CHECK_INTERVAL_SECONDS
|
|
388280
|
+
},
|
|
387020
388281
|
overrides: {}
|
|
387021
388282
|
};
|
|
387022
388283
|
autoUpdateTimer = null;
|
|
388284
|
+
/**
|
|
388285
|
+
* Re-arm hook for the availability poller, wired at boot (manual-boot). Kept
|
|
388286
|
+
* as a callback rather than a service reference so this module never imports
|
|
388287
|
+
* the scheduler's dependencies back.
|
|
388288
|
+
*/
|
|
388289
|
+
updateCheckRescheduler = null;
|
|
387023
388290
|
// -- Timing constants -----------------------------------------------------
|
|
387024
388291
|
// Short TTL — operators expect to see freshly-published versions
|
|
387025
388292
|
// within minutes of `npm publish`, not hours. The Addons page kicks
|
|
@@ -387033,7 +388300,7 @@ var require_addon_package_service = __commonJS({
|
|
|
387033
388300
|
// 10 minutes
|
|
387034
388301
|
static NPM_REGISTRY = "https://registry.npmjs.org";
|
|
387035
388302
|
static REGISTRY_TIMEOUT_MS = 1e4;
|
|
387036
|
-
constructor(loggingService, eventBusService, configService, addonRegistry, notificationService, toastService) {
|
|
388303
|
+
constructor(loggingService, eventBusService, configService, addonRegistry, notificationService, toastService, updateAvailabilityStore) {
|
|
387037
388304
|
this.loggingService = loggingService;
|
|
387038
388305
|
this.eventBusService = eventBusService;
|
|
387039
388306
|
this.configService = configService;
|
|
@@ -387041,10 +388308,7 @@ var require_addon_package_service = __commonJS({
|
|
|
387041
388308
|
this.notificationService = notificationService;
|
|
387042
388309
|
this.toastService = toastService;
|
|
387043
388310
|
this.logger = this.loggingService.createLogger("AddonPackageService");
|
|
387044
|
-
this.updateAvailability = new update_availability_emitter_js_1.UpdateAvailabilityEmitter(this.eventBusService, {
|
|
387045
|
-
type: "core",
|
|
387046
|
-
id: "addon-package-service"
|
|
387047
|
-
});
|
|
388311
|
+
this.updateAvailability = new update_availability_emitter_js_1.UpdateAvailabilityEmitter(this.eventBusService, { type: "core", id: "addon-package-service" }, updateAvailabilityStore);
|
|
387048
388312
|
try {
|
|
387049
388313
|
const addonsDir = this.resolveAddonsDir();
|
|
387050
388314
|
const workspacePackagesDir = (0, system_1.detectWorkspacePackagesDir)(__dirname);
|
|
@@ -387409,8 +388673,8 @@ var require_addon_package_service = __commonJS({
|
|
|
387409
388673
|
}
|
|
387410
388674
|
this.logger.info("Checking for package updates...");
|
|
387411
388675
|
const updates = [];
|
|
387412
|
-
const
|
|
387413
|
-
updates.push(...
|
|
388676
|
+
const sweep = await this.checkAddonPackageUpdates();
|
|
388677
|
+
updates.push(...sweep.updates);
|
|
387414
388678
|
this.logger.info("Found package updates", {
|
|
387415
388679
|
meta: {
|
|
387416
388680
|
count: updates.length,
|
|
@@ -387425,12 +388689,7 @@ var require_addon_package_service = __commonJS({
|
|
|
387425
388689
|
updates,
|
|
387426
388690
|
expiresAt: now + _AddonPackageService.UPDATE_CACHE_TTL_MS
|
|
387427
388691
|
};
|
|
387428
|
-
this.
|
|
387429
|
-
target: "addon",
|
|
387430
|
-
packageName: update.name,
|
|
387431
|
-
currentVersion: update.currentVersion,
|
|
387432
|
-
latestVersion: update.latestVersion
|
|
387433
|
-
})));
|
|
388692
|
+
this.publishAddonAvailability(updates, sweep.failures);
|
|
387434
388693
|
return updates;
|
|
387435
388694
|
}
|
|
387436
388695
|
/** Clear the cached update check results */
|
|
@@ -387454,12 +388713,22 @@ var require_addon_package_service = __commonJS({
|
|
|
387454
388713
|
seen.set(pkg.name, pkg.version);
|
|
387455
388714
|
}
|
|
387456
388715
|
const updates = [];
|
|
388716
|
+
let failures = 0;
|
|
387457
388717
|
await Promise.all([...seen].map(async ([name, version]) => {
|
|
387458
388718
|
if (!this.isAllowedPackage(name))
|
|
387459
388719
|
return;
|
|
387460
388720
|
if (exports.AUTO_UPDATE_EXCLUDED_PACKAGES.has(name))
|
|
387461
388721
|
return;
|
|
387462
|
-
const
|
|
388722
|
+
const lookup = await this.lookupLatestVersion(name);
|
|
388723
|
+
if (!lookup.ok) {
|
|
388724
|
+
failures += 1;
|
|
388725
|
+
this.logger.warn("Registry lookup failed for installed package", {
|
|
388726
|
+
tags: { nodeId: nodeId ?? "hub" },
|
|
388727
|
+
meta: { name, error: lookup.error }
|
|
388728
|
+
});
|
|
388729
|
+
return;
|
|
388730
|
+
}
|
|
388731
|
+
const latestVersion = lookup.latestVersion;
|
|
387463
388732
|
if (latestVersion === null || !isVersionNewer(latestVersion, version))
|
|
387464
388733
|
return;
|
|
387465
388734
|
const category = this.categorize(name);
|
|
@@ -387471,13 +388740,7 @@ var require_addon_package_service = __commonJS({
|
|
|
387471
388740
|
requiresRestart: category === "core"
|
|
387472
388741
|
});
|
|
387473
388742
|
}));
|
|
387474
|
-
this.
|
|
387475
|
-
target: "addon",
|
|
387476
|
-
packageName: update.name,
|
|
387477
|
-
currentVersion: update.currentVersion,
|
|
387478
|
-
latestVersion: update.latestVersion,
|
|
387479
|
-
...nodeId !== void 0 ? { nodeId } : {}
|
|
387480
|
-
})), nodeId);
|
|
388743
|
+
this.publishAddonAvailability(updates, failures, nodeId);
|
|
387481
388744
|
return updates;
|
|
387482
388745
|
}
|
|
387483
388746
|
/**
|
|
@@ -387854,10 +389117,15 @@ var require_addon_package_service = __commonJS({
|
|
|
387854
389117
|
*/
|
|
387855
389118
|
restartServer(requestedBy) {
|
|
387856
389119
|
this.logger.info("Server restart requested -- initiating graceful shutdown");
|
|
389120
|
+
const toVersion = toVersionFromRestartReason(requestedBy);
|
|
387857
389121
|
const payload = {
|
|
387858
|
-
kind: "manual",
|
|
389122
|
+
kind: isServerUpdateRequest(requestedBy) ? "framework-update" : "manual",
|
|
387859
389123
|
requestedAt: Date.now(),
|
|
387860
|
-
...requestedBy !== void 0 ? { requestedBy } : {}
|
|
389124
|
+
...requestedBy !== void 0 ? { requestedBy } : {},
|
|
389125
|
+
...isServerUpdateRequest(requestedBy) ? {
|
|
389126
|
+
packageName: "@camstack/server",
|
|
389127
|
+
...toVersion !== void 0 ? { toVersion } : {}
|
|
389128
|
+
} : {}
|
|
387861
389129
|
};
|
|
387862
389130
|
try {
|
|
387863
389131
|
(0, system_1.writePendingRestart)(this.resolveDataDir(), payload);
|
|
@@ -387899,6 +389167,7 @@ var require_addon_package_service = __commonJS({
|
|
|
387899
389167
|
const hubPkgDir = (0, package_dir_utils_js_1.resolveHubClosurePackageDir)(packageName);
|
|
387900
389168
|
const buildId = hubPkgDir !== null ? (0, package_dir_utils_js_1.computeDistBuildId)(path.join(hubPkgDir, "dist")) : null;
|
|
387901
389169
|
let latestVersion = null;
|
|
389170
|
+
let lookupFailed = false;
|
|
387902
389171
|
try {
|
|
387903
389172
|
const args = [
|
|
387904
389173
|
"view",
|
|
@@ -387910,21 +389179,65 @@ var require_addon_package_service = __commonJS({
|
|
|
387910
389179
|
const trimmed = stdout.trim();
|
|
387911
389180
|
latestVersion = trimmed.length > 0 ? trimmed : null;
|
|
387912
389181
|
} catch (err) {
|
|
389182
|
+
lookupFailed = true;
|
|
387913
389183
|
this.logger.debug("listFrameworkPackages: npm view failed", {
|
|
387914
389184
|
meta: { packageName, error: (0, types_1.errMsg)(err) }
|
|
387915
389185
|
});
|
|
387916
389186
|
}
|
|
387917
|
-
const hasUpdate = latestVersion !== null && currentVersion !== "unknown" && latestVersion
|
|
389187
|
+
const hasUpdate = latestVersion !== null && currentVersion !== "unknown" && isVersionNewer(latestVersion, currentVersion);
|
|
387918
389188
|
return {
|
|
387919
389189
|
packageName,
|
|
387920
389190
|
currentVersion,
|
|
387921
389191
|
latestVersion,
|
|
387922
389192
|
hasUpdate,
|
|
387923
389193
|
buildId,
|
|
389194
|
+
lookupFailed,
|
|
387924
389195
|
...description !== void 0 ? { description } : {}
|
|
387925
389196
|
};
|
|
387926
389197
|
}));
|
|
387927
|
-
|
|
389198
|
+
this.publishFrameworkAvailability(rows);
|
|
389199
|
+
return rows.map(({ lookupFailed: _lookupFailed, ...row }) => row);
|
|
389200
|
+
}
|
|
389201
|
+
/**
|
|
389202
|
+
* Announce framework updates. This surface SHOWED `@camstack/system` had an
|
|
389203
|
+
* update and emitted nothing, so the operator was never told — it was the
|
|
389204
|
+
* only discovery path with no publish at all.
|
|
389205
|
+
*
|
|
389206
|
+
* Two rules it must not break:
|
|
389207
|
+
* - **Strictly newer only.** `hasUpdate` is `latest !== current`, which is
|
|
389208
|
+
* true for a hub running a `-dev.<timestamp>` build AHEAD of the npm tag.
|
|
389209
|
+
* Announcing that would be telling the operator to downgrade.
|
|
389210
|
+
* - **A failed lookup publishes nothing.** Not even an empty snapshot: an
|
|
389211
|
+
* empty snapshot means "up to date" and would clear the dedup state, so
|
|
389212
|
+
* the next successful poll would re-announce everything.
|
|
389213
|
+
*/
|
|
389214
|
+
publishFrameworkAvailability(rows) {
|
|
389215
|
+
const failed = rows.filter((row) => row.lookupFailed);
|
|
389216
|
+
if (failed.length > 0) {
|
|
389217
|
+
this.logger.warn("Framework update check incomplete \u2014 availability not published", {
|
|
389218
|
+
meta: { packages: failed.map((row) => row.packageName) }
|
|
389219
|
+
});
|
|
389220
|
+
return;
|
|
389221
|
+
}
|
|
389222
|
+
const candidates = [];
|
|
389223
|
+
for (const row of rows) {
|
|
389224
|
+
if (row.latestVersion === null || row.currentVersion === "unknown")
|
|
389225
|
+
continue;
|
|
389226
|
+
if (!isVersionNewer(row.latestVersion, row.currentVersion))
|
|
389227
|
+
continue;
|
|
389228
|
+
candidates.push({
|
|
389229
|
+
target: "server",
|
|
389230
|
+
packageName: row.packageName,
|
|
389231
|
+
currentVersion: row.currentVersion,
|
|
389232
|
+
latestVersion: row.latestVersion,
|
|
389233
|
+
nodeId: this.resolveNodeId()
|
|
389234
|
+
});
|
|
389235
|
+
}
|
|
389236
|
+
this.updateAvailability.publishSnapshot("server", candidates, this.resolveNodeId());
|
|
389237
|
+
}
|
|
389238
|
+
/** Node id stamped on availability events emitted by THIS process. */
|
|
389239
|
+
resolveNodeId() {
|
|
389240
|
+
return process.env["CAMSTACK_NODE_ID"] ?? "hub";
|
|
387928
389241
|
}
|
|
387929
389242
|
// =========================================================================
|
|
387930
389243
|
// Reload
|
|
@@ -387956,21 +389269,37 @@ var require_addon_package_service = __commonJS({
|
|
|
387956
389269
|
// =========================================================================
|
|
387957
389270
|
// Auto-update settings
|
|
387958
389271
|
// =========================================================================
|
|
389272
|
+
/**
|
|
389273
|
+
* Register the availability poller's re-arm hook. Called once at boot so a
|
|
389274
|
+
* `setAutoUpdateSettings` write takes effect without a restart.
|
|
389275
|
+
*/
|
|
389276
|
+
setUpdateCheckRescheduler(reschedule) {
|
|
389277
|
+
this.updateCheckRescheduler = reschedule;
|
|
389278
|
+
}
|
|
387959
389279
|
/** Get global auto-update settings */
|
|
387960
389280
|
getAutoUpdateSettings() {
|
|
387961
389281
|
return { ...this.autoUpdateConfig.global };
|
|
387962
389282
|
}
|
|
387963
389283
|
/** Set global auto-update settings and restart the timer */
|
|
387964
|
-
async setAutoUpdateSettings(channel, intervalSeconds) {
|
|
389284
|
+
async setAutoUpdateSettings(channel, intervalSeconds, updateCheckIntervalSeconds) {
|
|
389285
|
+
const previousCheckInterval = this.autoUpdateConfig.global.updateCheckIntervalSeconds;
|
|
389286
|
+
const nextCheckInterval = updateCheckIntervalSeconds === void 0 ? previousCheckInterval : (0, update_check_scheduler_js_1.clampUpdateCheckInterval)(updateCheckIntervalSeconds);
|
|
387965
389287
|
this.autoUpdateConfig = {
|
|
387966
389288
|
...this.autoUpdateConfig,
|
|
387967
389289
|
global: {
|
|
387968
389290
|
channel,
|
|
387969
|
-
intervalSeconds: intervalSeconds ?? this.autoUpdateConfig.global.intervalSeconds
|
|
389291
|
+
intervalSeconds: intervalSeconds ?? this.autoUpdateConfig.global.intervalSeconds,
|
|
389292
|
+
updateCheckIntervalSeconds: nextCheckInterval
|
|
387970
389293
|
}
|
|
387971
389294
|
};
|
|
387972
389295
|
this.saveAutoUpdateConfig();
|
|
387973
389296
|
this.scheduleAutoUpdate();
|
|
389297
|
+
if (updateCheckIntervalSeconds === void 0)
|
|
389298
|
+
return;
|
|
389299
|
+
this.logger.info("Update-check interval changed", {
|
|
389300
|
+
meta: { from: previousCheckInterval, to: nextCheckInterval }
|
|
389301
|
+
});
|
|
389302
|
+
this.updateCheckRescheduler?.();
|
|
387974
389303
|
}
|
|
387975
389304
|
/** Get per-addon auto-update override */
|
|
387976
389305
|
getAddonAutoUpdate(addonId) {
|
|
@@ -388101,10 +389430,12 @@ var require_addon_package_service = __commonJS({
|
|
|
388101
389430
|
const global2 = asRecord(raw["global"]);
|
|
388102
389431
|
const channel = asString(global2["channel"]);
|
|
388103
389432
|
const validChannel = channel === "latest" || channel === "beta" ? channel : "off";
|
|
389433
|
+
const rawCheckInterval = global2["updateCheckIntervalSeconds"];
|
|
388104
389434
|
return {
|
|
388105
389435
|
global: {
|
|
388106
389436
|
channel: validChannel,
|
|
388107
|
-
intervalSeconds: typeof global2["intervalSeconds"] === "number" ? global2["intervalSeconds"] : 3600
|
|
389437
|
+
intervalSeconds: typeof global2["intervalSeconds"] === "number" ? global2["intervalSeconds"] : 3600,
|
|
389438
|
+
updateCheckIntervalSeconds: typeof rawCheckInterval === "number" ? (0, update_check_scheduler_js_1.clampUpdateCheckInterval)(rawCheckInterval) : update_check_scheduler_js_1.DEFAULT_UPDATE_CHECK_INTERVAL_SECONDS
|
|
388108
389439
|
},
|
|
388109
389440
|
overrides: Object.fromEntries(Object.entries(asRecord(raw["overrides"])).map(([k, v]) => {
|
|
388110
389441
|
const s = asString(v);
|
|
@@ -388119,7 +389450,14 @@ var require_addon_package_service = __commonJS({
|
|
|
388119
389450
|
meta: { error: (0, types_1.errMsg)(err) }
|
|
388120
389451
|
});
|
|
388121
389452
|
}
|
|
388122
|
-
return {
|
|
389453
|
+
return {
|
|
389454
|
+
global: {
|
|
389455
|
+
channel: "off",
|
|
389456
|
+
intervalSeconds: 21600,
|
|
389457
|
+
updateCheckIntervalSeconds: update_check_scheduler_js_1.DEFAULT_UPDATE_CHECK_INTERVAL_SECONDS
|
|
389458
|
+
},
|
|
389459
|
+
overrides: {}
|
|
389460
|
+
};
|
|
388123
389461
|
}
|
|
388124
389462
|
/** Save auto-update config to disk */
|
|
388125
389463
|
saveAutoUpdateConfig() {
|
|
@@ -388144,12 +389482,17 @@ var require_addon_package_service = __commonJS({
|
|
|
388144
389482
|
/**
|
|
388145
389483
|
* Check addon packages for updates by reading installed versions from
|
|
388146
389484
|
* data/addons/{name}/package.json and comparing against npm registry.
|
|
389485
|
+
*
|
|
389486
|
+
* Reports `failures` alongside the diff: a registry lookup that could not be
|
|
389487
|
+
* MADE is not evidence that a package is up to date, and the availability
|
|
389488
|
+
* publish branches on it.
|
|
388147
389489
|
*/
|
|
388148
389490
|
async checkAddonPackageUpdates() {
|
|
388149
389491
|
const addonsDir = this.resolveAddonsDir();
|
|
388150
389492
|
const updates = [];
|
|
389493
|
+
let failures = 0;
|
|
388151
389494
|
if (!fs.existsSync(addonsDir))
|
|
388152
|
-
return updates;
|
|
389495
|
+
return { updates, failures };
|
|
388153
389496
|
const pkgJsonPaths = [];
|
|
388154
389497
|
const topDirs = fs.readdirSync(addonsDir, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
388155
389498
|
for (const dir of topDirs) {
|
|
@@ -388186,7 +389529,16 @@ var require_addon_package_service = __commonJS({
|
|
|
388186
389529
|
if (source === "workspace")
|
|
388187
389530
|
continue;
|
|
388188
389531
|
}
|
|
388189
|
-
const
|
|
389532
|
+
const lookup = await this.lookupLatestVersion(name);
|
|
389533
|
+
if (!lookup.ok) {
|
|
389534
|
+
failures += 1;
|
|
389535
|
+
this.logger.warn("Registry lookup failed for installed addon package", {
|
|
389536
|
+
tags: { nodeId: this.resolveNodeId() },
|
|
389537
|
+
meta: { name, error: lookup.error }
|
|
389538
|
+
});
|
|
389539
|
+
continue;
|
|
389540
|
+
}
|
|
389541
|
+
const latestVersion = lookup.latestVersion;
|
|
388190
389542
|
if (!latestVersion)
|
|
388191
389543
|
continue;
|
|
388192
389544
|
if (isVersionNewer(latestVersion, version)) {
|
|
@@ -388199,13 +389551,14 @@ var require_addon_package_service = __commonJS({
|
|
|
388199
389551
|
});
|
|
388200
389552
|
}
|
|
388201
389553
|
} catch (error) {
|
|
389554
|
+
failures += 1;
|
|
388202
389555
|
const msg = (0, types_1.errMsg)(error);
|
|
388203
|
-
this.logger.
|
|
389556
|
+
this.logger.warn("Failed to check updates for addon", {
|
|
388204
389557
|
meta: { pkgJsonPath, error: msg }
|
|
388205
389558
|
});
|
|
388206
389559
|
}
|
|
388207
389560
|
}
|
|
388208
|
-
return updates;
|
|
389561
|
+
return { updates, failures };
|
|
388209
389562
|
}
|
|
388210
389563
|
// =========================================================================
|
|
388211
389564
|
// Private: npm registry helpers
|
|
@@ -388216,7 +389569,7 @@ var require_addon_package_service = __commonJS({
|
|
|
388216
389569
|
* Honours `CAMSTACK_NPM_REGISTRY` so update checks resolve against
|
|
388217
389570
|
* the same registry the installer/pack paths use. Without this, a
|
|
388218
389571
|
* per-node `listUpdates` (which diffs an agent's roster via
|
|
388219
|
-
* `checkUpdatesForInstalled` → `
|
|
389572
|
+
* `checkUpdatesForInstalled` → `lookupLatestVersion`) would bypass a
|
|
388220
389573
|
* private registry — including the e2e harness's verdaccio — and
|
|
388221
389574
|
* silently report "no update" for packages that only exist there.
|
|
388222
389575
|
* Trailing slashes are stripped so the `${base}/${name}` join is clean.
|
|
@@ -388226,28 +389579,60 @@ var require_addon_package_service = __commonJS({
|
|
|
388226
389579
|
const base = override && override.length > 0 ? override : _AddonPackageService.NPM_REGISTRY;
|
|
388227
389580
|
return base.replace(/\/+$/, "");
|
|
388228
389581
|
}
|
|
388229
|
-
/**
|
|
388230
|
-
|
|
389582
|
+
/**
|
|
389583
|
+
* Ask the registry for a package's latest version, distinguishing "there is
|
|
389584
|
+
* no such package" (a definitive answer) from "I could not ask" (a failure).
|
|
389585
|
+
* The difference decides whether an availability SNAPSHOT may be published —
|
|
389586
|
+
* see {@link RegistryLookup}.
|
|
389587
|
+
*/
|
|
389588
|
+
async lookupLatestVersion(packageName) {
|
|
388231
389589
|
try {
|
|
388232
389590
|
const encodedName = packageName.replace("/", "%2F");
|
|
388233
389591
|
const url = `${this.resolveRegistryBase()}/${encodedName}/latest`;
|
|
388234
389592
|
const response = await fetch(url, {
|
|
388235
389593
|
signal: AbortSignal.timeout(_AddonPackageService.REGISTRY_TIMEOUT_MS)
|
|
388236
389594
|
});
|
|
389595
|
+
if (response.status === 404)
|
|
389596
|
+
return { ok: true, latestVersion: null };
|
|
388237
389597
|
if (!response.ok) {
|
|
388238
389598
|
this.logger.debug("Registry returned non-ok status", {
|
|
388239
389599
|
meta: { packageName, status: response.status }
|
|
388240
389600
|
});
|
|
388241
|
-
return
|
|
389601
|
+
return { ok: false, error: `registry status ${response.status}` };
|
|
388242
389602
|
}
|
|
388243
389603
|
const data = await fetchJsonObject(response);
|
|
388244
389604
|
const version = asString(data["version"]);
|
|
388245
|
-
return version || null;
|
|
389605
|
+
return { ok: true, latestVersion: version || null };
|
|
388246
389606
|
} catch (error) {
|
|
388247
|
-
|
|
388248
|
-
|
|
388249
|
-
|
|
389607
|
+
return { ok: false, error: (0, types_1.errMsg)(error) };
|
|
389608
|
+
}
|
|
389609
|
+
}
|
|
389610
|
+
/**
|
|
389611
|
+
* Announce the addon-update set for one scope (hub or a node).
|
|
389612
|
+
*
|
|
389613
|
+
* `failures > 0` means at least one registry lookup could not be made, so
|
|
389614
|
+
* the set is INCOMPLETE — publish it as candidates (additive) rather than a
|
|
389615
|
+
* snapshot (authoritative). A snapshot built from a partial sweep silently
|
|
389616
|
+
* marks the unreachable packages "up to date", clears their dedup state and
|
|
389617
|
+
* re-announces them on the next good poll.
|
|
389618
|
+
*/
|
|
389619
|
+
publishAddonAvailability(updates, failures, nodeId) {
|
|
389620
|
+
const candidates = updates.map((update) => ({
|
|
389621
|
+
target: "addon",
|
|
389622
|
+
packageName: update.name,
|
|
389623
|
+
currentVersion: update.currentVersion,
|
|
389624
|
+
latestVersion: update.latestVersion,
|
|
389625
|
+
...nodeId !== void 0 ? { nodeId } : {}
|
|
389626
|
+
}));
|
|
389627
|
+
if (failures > 0) {
|
|
389628
|
+
this.logger.warn("Addon update check incomplete \u2014 availability published as partial", {
|
|
389629
|
+
tags: { nodeId: nodeId ?? "hub" },
|
|
389630
|
+
meta: { failures, published: candidates.length }
|
|
389631
|
+
});
|
|
389632
|
+
this.updateAvailability.publishCandidates(candidates);
|
|
389633
|
+
return;
|
|
388250
389634
|
}
|
|
389635
|
+
this.updateAvailability.publishSnapshot("addon", candidates, nodeId);
|
|
388251
389636
|
}
|
|
388252
389637
|
/** Fetch npm search results for camstack addon packages (cached 5 min) */
|
|
388253
389638
|
async fetchSearchFromNpm() {
|
|
@@ -388397,6 +389782,15 @@ var require_addon_package_service = __commonJS({
|
|
|
388397
389782
|
}
|
|
388398
389783
|
};
|
|
388399
389784
|
exports.AddonPackageService = AddonPackageService;
|
|
389785
|
+
function isServerUpdateRequest(requestedBy) {
|
|
389786
|
+
return requestedBy !== void 0 && requestedBy.startsWith("server-update:");
|
|
389787
|
+
}
|
|
389788
|
+
function toVersionFromRestartReason(requestedBy) {
|
|
389789
|
+
if (requestedBy === void 0)
|
|
389790
|
+
return void 0;
|
|
389791
|
+
const match = /@([^@\s]+)$/.exec(requestedBy);
|
|
389792
|
+
return match?.[1];
|
|
389793
|
+
}
|
|
388400
389794
|
async function extractTgzStripped(tgz, destDir) {
|
|
388401
389795
|
fs.mkdirSync(destDir, { recursive: true });
|
|
388402
389796
|
const tmpFile = path.join(os.tmpdir(), `camstack-tgz-${(0, node_crypto_1.randomUUID)()}.tgz`);
|
|
@@ -396513,6 +397907,30 @@ var require_integration_id_backfill = __commonJS({
|
|
|
396513
397907
|
}
|
|
396514
397908
|
});
|
|
396515
397909
|
|
|
397910
|
+
// ../../server/backend/dist/core/updates/agent-installed-packages.js
|
|
397911
|
+
var require_agent_installed_packages = __commonJS({
|
|
397912
|
+
"../../server/backend/dist/core/updates/agent-installed-packages.js"(exports) {
|
|
397913
|
+
"use strict";
|
|
397914
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
397915
|
+
exports.AGENT_STATUS_TIMEOUT_MS = void 0;
|
|
397916
|
+
exports.fetchAgentInstalledPackages = fetchAgentInstalledPackages;
|
|
397917
|
+
var addon_package_service_js_1 = require_addon_package_service();
|
|
397918
|
+
exports.AGENT_STATUS_TIMEOUT_MS = 5e3;
|
|
397919
|
+
async function fetchAgentInstalledPackages(broker, nodeId) {
|
|
397920
|
+
const status = await broker.call("$agent.status", {}, { nodeID: nodeId, timeout: exports.AGENT_STATUS_TIMEOUT_MS });
|
|
397921
|
+
const out = [];
|
|
397922
|
+
for (const addon of status.addons ?? []) {
|
|
397923
|
+
if (typeof addon.packageName !== "string" || typeof addon.version !== "string")
|
|
397924
|
+
continue;
|
|
397925
|
+
if ((0, addon_package_service_js_1.isFrameworkPackage)(addon.packageName))
|
|
397926
|
+
continue;
|
|
397927
|
+
out.push({ name: addon.packageName, version: addon.version });
|
|
397928
|
+
}
|
|
397929
|
+
return out;
|
|
397930
|
+
}
|
|
397931
|
+
}
|
|
397932
|
+
});
|
|
397933
|
+
|
|
396516
397934
|
// ../../server/backend/dist/api/core/collection-preference.js
|
|
396517
397935
|
var require_collection_preference = __commonJS({
|
|
396518
397936
|
"../../server/backend/dist/api/core/collection-preference.js"(exports) {
|
|
@@ -396888,6 +398306,7 @@ var require_post_boot_service = __commonJS({
|
|
|
396888
398306
|
const payload = {
|
|
396889
398307
|
kind: marker.kind,
|
|
396890
398308
|
requestedAt: marker.requestedAt,
|
|
398309
|
+
nodeId: process.env["CAMSTACK_NODE_ID"] ?? "hub",
|
|
396891
398310
|
...marker.packageName !== void 0 ? { packageName: marker.packageName } : {},
|
|
396892
398311
|
...marker.fromVersion !== void 0 ? { fromVersion: marker.fromVersion } : {},
|
|
396893
398312
|
...marker.toVersion !== void 0 ? { toVersion: marker.toVersion } : {},
|
|
@@ -396971,6 +398390,7 @@ var require_cap_providers = __commonJS({
|
|
|
396971
398390
|
var integration_id_backfill_1 = require_integration_id_backfill();
|
|
396972
398391
|
var addon_package_service_js_1 = require_addon_package_service();
|
|
396973
398392
|
var lifecycle_runner_singleton_js_1 = require_lifecycle_runner_singleton();
|
|
398393
|
+
var agent_installed_packages_js_1 = require_agent_installed_packages();
|
|
396974
398394
|
var collection_preference_js_1 = require_collection_preference();
|
|
396975
398395
|
var site_location_js_1 = require_site_location();
|
|
396976
398396
|
function getRetention(registry) {
|
|
@@ -397854,18 +399274,6 @@ var require_cap_providers = __commonJS({
|
|
|
397854
399274
|
function isHubNode(nodeId) {
|
|
397855
399275
|
return nodeId === "hub" || nodeId.startsWith("hub/");
|
|
397856
399276
|
}
|
|
397857
|
-
async function fetchAgentInstalledPackages(broker, nodeId) {
|
|
397858
|
-
const status = await broker.call("$agent.status", {}, { nodeID: nodeId, timeout: 5e3 });
|
|
397859
|
-
const out = [];
|
|
397860
|
-
for (const a of status.addons ?? []) {
|
|
397861
|
-
if (typeof a.packageName !== "string" || typeof a.version !== "string")
|
|
397862
|
-
continue;
|
|
397863
|
-
if ((0, addon_package_service_js_1.isFrameworkPackage)(a.packageName))
|
|
397864
|
-
continue;
|
|
397865
|
-
out.push({ name: a.packageName, version: a.version });
|
|
397866
|
-
}
|
|
397867
|
-
return out;
|
|
397868
|
-
}
|
|
397869
399277
|
function buildAddonsProvider(ar, ps, ls, moleculer, configService, ctx) {
|
|
397870
399278
|
const broker = moleculer.broker;
|
|
397871
399279
|
const frameworkAllowSet = /* @__PURE__ */ new Set([addon_package_service_js_1.SYSTEM_PACKAGE]);
|
|
@@ -397906,7 +399314,7 @@ var require_cap_providers = __commonJS({
|
|
|
397906
399314
|
},
|
|
397907
399315
|
listUpdates: async (input) => {
|
|
397908
399316
|
const nodeId = input.nodeId;
|
|
397909
|
-
const updates = nodeId === void 0 || isHubNode(nodeId) ? await ps.checkUpdates() : await ps.checkUpdatesForInstalled(await fetchAgentInstalledPackages(broker, nodeId), nodeId);
|
|
399317
|
+
const updates = nodeId === void 0 || isHubNode(nodeId) ? await ps.checkUpdates() : await ps.checkUpdatesForInstalled(await (0, agent_installed_packages_js_1.fetchAgentInstalledPackages)(broker, nodeId), nodeId);
|
|
397910
399318
|
return updates.map((u) => ({ ...u, isSystem: frameworkAllowSet.has(u.name) }));
|
|
397911
399319
|
},
|
|
397912
399320
|
updatePackage: async (input) => {
|
|
@@ -397935,7 +399343,7 @@ var require_cap_providers = __commonJS({
|
|
|
397935
399343
|
const nodeId = input.nodeId;
|
|
397936
399344
|
if (nodeId === void 0 || isHubNode(nodeId))
|
|
397937
399345
|
return ps.checkUpdates(true);
|
|
397938
|
-
const installed = await fetchAgentInstalledPackages(broker, nodeId);
|
|
399346
|
+
const installed = await (0, agent_installed_packages_js_1.fetchAgentInstalledPackages)(broker, nodeId);
|
|
397939
399347
|
return ps.checkUpdatesForInstalled(installed, nodeId);
|
|
397940
399348
|
},
|
|
397941
399349
|
restartServer: async () => ps.restartServer(ctx.user?.username ?? ctx.user?.id),
|
|
@@ -397996,7 +399404,7 @@ var require_cap_providers = __commonJS({
|
|
|
397996
399404
|
return { success: true };
|
|
397997
399405
|
},
|
|
397998
399406
|
getAutoUpdateSettings: async () => ps.getAutoUpdateSettings(),
|
|
397999
|
-
setAutoUpdateSettings: async (input) => ps.setAutoUpdateSettings(input.channel, input.intervalSeconds),
|
|
399407
|
+
setAutoUpdateSettings: async (input) => ps.setAutoUpdateSettings(input.channel, input.intervalSeconds, input.updateCheckIntervalSeconds),
|
|
398000
399408
|
getAddonAutoUpdate: async (input) => ps.getAddonAutoUpdate(input.addonId),
|
|
398001
399409
|
setAddonAutoUpdate: async (input) => ps.setAddonAutoUpdate(input.addonId, input.channel),
|
|
398002
399410
|
applyAutoUpdateToAll: async (input) => {
|
|
@@ -398243,6 +399651,12 @@ var require_event_bus_proxy_router = __commonJS({
|
|
|
398243
399651
|
}
|
|
398244
399652
|
function createEventBusProxyRouter(eventBus) {
|
|
398245
399653
|
return (0, trpc_middleware_js_1.trpcRouter)({
|
|
399654
|
+
// ADMIN-ONLY. `emit` injects a fully-attributed SystemEvent onto the hub's
|
|
399655
|
+
// real EventBus — including a spoofable `source` (a fake `device`/`addon`
|
|
399656
|
+
// origin). Forked addons that legitimately emit reach this over the
|
|
399657
|
+
// trusted core-cap/UDS mesh, whose context is `createMeshTrpcContext`
|
|
399658
|
+
// (`isAdmin: true`), so gating it here withholds the surface from
|
|
399659
|
+
// non-admin HTTP/WS principals without blinding a worker.
|
|
398246
399660
|
emit: trpc_middleware_js_1.adminProcedure.input(SystemEventInputSchema).output(zod_1.z.object({ ok: zod_1.z.literal(true) })).mutation(({ input }) => {
|
|
398247
399661
|
eventBus.emit({
|
|
398248
399662
|
id: input.id,
|
|
@@ -404158,7 +405572,7 @@ var require_logging_service = __commonJS({
|
|
|
404158
405572
|
deviceNames = /* @__PURE__ */ new Map();
|
|
404159
405573
|
constructor(configService) {
|
|
404160
405574
|
const perAddonCapacity = configService.get("eventBus.perAddonLogBufferSize") ?? 5e3;
|
|
404161
|
-
const maxTotalEntries = configService.get("eventBus.maxTotalLogBufferSize")
|
|
405575
|
+
const maxTotalEntries = configService.get("eventBus.maxTotalLogBufferSize");
|
|
404162
405576
|
const pruneLevel = configService.get("eventBus.logBufferPruneLevel") ?? "debug";
|
|
404163
405577
|
super(perAddonCapacity, { maxTotalEntries, pruneLevel });
|
|
404164
405578
|
this.setDeviceNameLookup((id) => this.deviceNames.get(id) ?? null);
|
|
@@ -405500,11 +406914,12 @@ var require_server_update_service = __commonJS({
|
|
|
405500
406914
|
exports.ServerUpdateService = void 0;
|
|
405501
406915
|
var path = __importStar(__require("path"));
|
|
405502
406916
|
var index_js_1 = require_server_root();
|
|
405503
|
-
var system_exec_npm_js_1 = require_system_exec_npm();
|
|
405504
|
-
var system_ensure_prebuilds_js_1 = require_system_ensure_prebuilds();
|
|
405505
406917
|
var update_availability_emitter_js_1 = require_update_availability_emitter();
|
|
406918
|
+
var system_ensure_prebuilds_js_1 = require_system_ensure_prebuilds();
|
|
406919
|
+
var system_exec_npm_js_1 = require_system_exec_npm();
|
|
405506
406920
|
var ServerUpdateService = class extends index_js_1.RootUpdateService {
|
|
405507
406921
|
updateAvailability;
|
|
406922
|
+
nodeId;
|
|
405508
406923
|
constructor(options) {
|
|
405509
406924
|
const dataDir = path.resolve(options.dataDir ?? options.env?.["CAMSTACK_DATA"] ?? process.env["CAMSTACK_DATA"] ?? "camstack-data");
|
|
405510
406925
|
super({
|
|
@@ -405535,23 +406950,23 @@ var require_server_update_service = __commonJS({
|
|
|
405535
406950
|
env: options.env,
|
|
405536
406951
|
now: options.now
|
|
405537
406952
|
});
|
|
405538
|
-
this.updateAvailability = options.eventBus !== void 0 ? new update_availability_emitter_js_1.UpdateAvailabilityEmitter(options.eventBus, {
|
|
405539
|
-
|
|
405540
|
-
id: "server-update-service"
|
|
405541
|
-
}) : null;
|
|
406953
|
+
this.updateAvailability = options.eventBus !== void 0 ? new update_availability_emitter_js_1.UpdateAvailabilityEmitter(options.eventBus, { type: "core", id: "server-update-service" }, options.updateAvailabilityStore) : null;
|
|
406954
|
+
this.nodeId = options.nodeId ?? "hub";
|
|
405542
406955
|
}
|
|
405543
406956
|
async checkServerUpdate() {
|
|
405544
406957
|
const result = await super.checkServerUpdate();
|
|
405545
406958
|
if (result.error !== null)
|
|
405546
406959
|
return result;
|
|
406960
|
+
const nodeId = this.nodeId;
|
|
405547
406961
|
this.updateAvailability?.publishSnapshot("server", result.updateAvailable && result.runningVersion !== null && result.latestVersion !== null ? [
|
|
405548
406962
|
{
|
|
405549
406963
|
target: "server",
|
|
405550
406964
|
packageName: result.packageName,
|
|
405551
406965
|
currentVersion: result.runningVersion,
|
|
405552
|
-
latestVersion: result.latestVersion
|
|
406966
|
+
latestVersion: result.latestVersion,
|
|
406967
|
+
...nodeId !== void 0 ? { nodeId } : {}
|
|
405553
406968
|
}
|
|
405554
|
-
] : []);
|
|
406969
|
+
] : [], nodeId);
|
|
405555
406970
|
return result;
|
|
405556
406971
|
}
|
|
405557
406972
|
};
|
|
@@ -405572,6 +406987,82 @@ var require_storage_service = __commonJS({
|
|
|
405572
406987
|
}
|
|
405573
406988
|
});
|
|
405574
406989
|
|
|
406990
|
+
// ../../server/backend/dist/core/streaming/ttl-cache.js
|
|
406991
|
+
var require_ttl_cache = __commonJS({
|
|
406992
|
+
"../../server/backend/dist/core/streaming/ttl-cache.js"(exports) {
|
|
406993
|
+
"use strict";
|
|
406994
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
406995
|
+
exports.TtlCache = void 0;
|
|
406996
|
+
var TtlCache = class {
|
|
406997
|
+
entries = /* @__PURE__ */ new Map();
|
|
406998
|
+
ttlMs;
|
|
406999
|
+
maxEntries;
|
|
407000
|
+
now;
|
|
407001
|
+
constructor(options) {
|
|
407002
|
+
this.ttlMs = options.ttlMs;
|
|
407003
|
+
this.maxEntries = options.maxEntries;
|
|
407004
|
+
this.now = options.now ?? (() => Date.now());
|
|
407005
|
+
}
|
|
407006
|
+
/** The live value, or undefined when absent or expired. An expired entry is
|
|
407007
|
+
* DELETED here, not merely reported missing. */
|
|
407008
|
+
get(key) {
|
|
407009
|
+
const slot = this.entries.get(key);
|
|
407010
|
+
if (slot === void 0)
|
|
407011
|
+
return void 0;
|
|
407012
|
+
const at = this.now();
|
|
407013
|
+
if (at - slot.storedAt >= this.ttlMs) {
|
|
407014
|
+
this.entries.delete(key);
|
|
407015
|
+
return void 0;
|
|
407016
|
+
}
|
|
407017
|
+
slot.lastAccessAt = at;
|
|
407018
|
+
return slot.value;
|
|
407019
|
+
}
|
|
407020
|
+
/**
|
|
407021
|
+
* Store a value, then bring the map back inside both bounds.
|
|
407022
|
+
*
|
|
407023
|
+
* The sweep runs on WRITE and not on a timer on purpose: a timer would have
|
|
407024
|
+
* to be owned, unref'd and stopped by every holder of a cache, and a cache
|
|
407025
|
+
* that is never written to is a cache that is not growing.
|
|
407026
|
+
*/
|
|
407027
|
+
set(key, value) {
|
|
407028
|
+
const at = this.now();
|
|
407029
|
+
this.entries.set(key, { value, storedAt: at, lastAccessAt: at });
|
|
407030
|
+
this.sweepExpired(at);
|
|
407031
|
+
this.enforceMaxEntries(key);
|
|
407032
|
+
}
|
|
407033
|
+
delete(key) {
|
|
407034
|
+
this.entries.delete(key);
|
|
407035
|
+
}
|
|
407036
|
+
clear() {
|
|
407037
|
+
this.entries.clear();
|
|
407038
|
+
}
|
|
407039
|
+
/** Entries currently retained. The number that used to only go up. */
|
|
407040
|
+
size() {
|
|
407041
|
+
return this.entries.size;
|
|
407042
|
+
}
|
|
407043
|
+
sweepExpired(at) {
|
|
407044
|
+
for (const [key, slot] of this.entries) {
|
|
407045
|
+
if (at - slot.storedAt >= this.ttlMs)
|
|
407046
|
+
this.entries.delete(key);
|
|
407047
|
+
}
|
|
407048
|
+
}
|
|
407049
|
+
/** Evict least-recently-USED first. `protectedKey` is the entry just written —
|
|
407050
|
+
* evicting it would make `set` a no-op. */
|
|
407051
|
+
enforceMaxEntries(protectedKey) {
|
|
407052
|
+
if (this.entries.size <= this.maxEntries)
|
|
407053
|
+
return;
|
|
407054
|
+
const coldestFirst = [...this.entries.entries()].filter(([key]) => key !== protectedKey).toSorted((a, b) => a[1].lastAccessAt - b[1].lastAccessAt);
|
|
407055
|
+
for (const [key] of coldestFirst) {
|
|
407056
|
+
if (this.entries.size <= this.maxEntries)
|
|
407057
|
+
return;
|
|
407058
|
+
this.entries.delete(key);
|
|
407059
|
+
}
|
|
407060
|
+
}
|
|
407061
|
+
};
|
|
407062
|
+
exports.TtlCache = TtlCache;
|
|
407063
|
+
}
|
|
407064
|
+
});
|
|
407065
|
+
|
|
405575
407066
|
// ../../server/backend/dist/core/streaming/stream-probe.service.js
|
|
405576
407067
|
var require_stream_probe_service = __commonJS({
|
|
405577
407068
|
"../../server/backend/dist/core/streaming/stream-probe.service.js"(exports) {
|
|
@@ -405580,16 +407071,28 @@ var require_stream_probe_service = __commonJS({
|
|
|
405580
407071
|
exports.StreamProbeService = void 0;
|
|
405581
407072
|
var child_process_1 = __require("child_process");
|
|
405582
407073
|
var util_1 = __require("util");
|
|
407074
|
+
var ttl_cache_1 = require_ttl_cache();
|
|
405583
407075
|
var types_1 = require_dist9();
|
|
405584
407076
|
var execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
|
|
405585
407077
|
var CACHE_TTL_MS = 36e5;
|
|
405586
407078
|
var PROBE_TIMEOUT_MS = 5e3;
|
|
407079
|
+
var CACHE_MAX_ENTRIES = 512;
|
|
405587
407080
|
var CODEC_ALIASES = {
|
|
405588
407081
|
hevc: "h265"
|
|
405589
407082
|
};
|
|
405590
407083
|
var StreamProbeService = class {
|
|
405591
407084
|
logger;
|
|
405592
|
-
|
|
407085
|
+
/**
|
|
407086
|
+
* Probe results, bounded by BOTH a swept TTL and an entry ceiling.
|
|
407087
|
+
*
|
|
407088
|
+
* It used to be a plain `Map` whose age was consulted only when the same URL
|
|
407089
|
+
* was probed again — so an entry nobody asked about a second time was never
|
|
407090
|
+
* found expired and never removed. See {@link TtlCache}.
|
|
407091
|
+
*/
|
|
407092
|
+
cache = new ttl_cache_1.TtlCache({
|
|
407093
|
+
ttlMs: CACHE_TTL_MS,
|
|
407094
|
+
maxEntries: CACHE_MAX_ENTRIES
|
|
407095
|
+
});
|
|
405593
407096
|
constructor(loggingService) {
|
|
405594
407097
|
this.logger = loggingService.createLogger("StreamProbeService");
|
|
405595
407098
|
}
|
|
@@ -405601,12 +407104,11 @@ var require_stream_probe_service = __commonJS({
|
|
|
405601
407104
|
const force = options?.force ?? false;
|
|
405602
407105
|
if (!force) {
|
|
405603
407106
|
const cached = this.cache.get(url);
|
|
405604
|
-
if (cached
|
|
405605
|
-
return cached
|
|
405606
|
-
}
|
|
407107
|
+
if (cached)
|
|
407108
|
+
return cached;
|
|
405607
407109
|
}
|
|
405608
407110
|
const metadata = await this.runProbe(url);
|
|
405609
|
-
this.cache.set(url,
|
|
407111
|
+
this.cache.set(url, metadata);
|
|
405610
407112
|
return metadata;
|
|
405611
407113
|
}
|
|
405612
407114
|
/**
|
|
@@ -406458,6 +407960,9 @@ var require_manual_boot = __commonJS({
|
|
|
406458
407960
|
var storage_service_1 = require_storage_service();
|
|
406459
407961
|
var stream_probe_service_1 = require_stream_probe_service();
|
|
406460
407962
|
var topology_emitter_service_1 = require_topology_emitter_service();
|
|
407963
|
+
var update_availability_store_js_1 = require_update_availability_store();
|
|
407964
|
+
var agent_installed_packages_js_1 = require_agent_installed_packages();
|
|
407965
|
+
var update_check_scheduler_js_1 = require_update_check_scheduler();
|
|
406461
407966
|
var ServiceContainer = class {
|
|
406462
407967
|
services = /* @__PURE__ */ new Map();
|
|
406463
407968
|
register(ctor, instance) {
|
|
@@ -406503,7 +408008,8 @@ var require_manual_boot = __commonJS({
|
|
|
406503
408008
|
agentRegistryService.setAddonRegistry(addonRegistryService);
|
|
406504
408009
|
agentRegistryService.setClusterNodeHistoryStore(new cluster_node_history_store_1.ClusterNodeHistoryStore(() => capabilityService.getRegistry()?.getSingleton("settings-store") ?? null, loggingService.createLogger("cluster-node-history")));
|
|
406505
408010
|
const addonWidgetsService = new addon_widgets_service_1.AddonWidgetsService(loggingService, capabilityService, addonRegistryService);
|
|
406506
|
-
const
|
|
408011
|
+
const availabilityDataDir = path.resolve(process.env["CAMSTACK_DATA"] ?? "camstack-data");
|
|
408012
|
+
const addonPackageService = new addon_package_service_1.AddonPackageService(loggingService, eventBusService, configService, addonRegistryService, notificationWrapper, toastWrapper, new update_availability_store_js_1.FileUpdateAvailabilityStore(availabilityDataDir, "addon-packages", loggingService.createLogger("UpdateAvailability")));
|
|
406507
408013
|
const backfillLogger = loggingService.createLogger("agent-backfill");
|
|
406508
408014
|
agentRegistryService.setAddonBackfill({
|
|
406509
408015
|
pack: async (name, version) => {
|
|
@@ -406556,10 +408062,40 @@ var require_manual_boot = __commonJS({
|
|
|
406556
408062
|
const serverUpdateService = new server_update_service_1.ServerUpdateService({
|
|
406557
408063
|
logger: loggingService.createLogger("ServerUpdate"),
|
|
406558
408064
|
restartServer: (requestedBy) => addonPackageService.restartServer(requestedBy),
|
|
406559
|
-
eventBus: eventBusService
|
|
408065
|
+
eventBus: eventBusService,
|
|
408066
|
+
updateAvailabilityStore: new update_availability_store_js_1.FileUpdateAvailabilityStore(availabilityDataDir, "server-update", loggingService.createLogger("UpdateAvailability"))
|
|
406560
408067
|
});
|
|
406561
408068
|
const topologyEmitterService = new topology_emitter_service_1.TopologyEmitterService(eventBusService, agentRegistryService, addonRegistryService, (0, cap_providers_1.createNodeRootPackageLookup)(moleculerService, serverUpdateService));
|
|
406562
408069
|
const postBootService = new post_boot_service_1.PostBootService(addonRegistryService, eventBusService, loggingService);
|
|
408070
|
+
const updateCheckLogger = loggingService.createLogger("UpdateCheck");
|
|
408071
|
+
const updateCheckScheduler = new update_check_scheduler_js_1.UpdateCheckScheduler({
|
|
408072
|
+
logger: updateCheckLogger,
|
|
408073
|
+
getIntervalSeconds: () => addonPackageService.getAutoUpdateSettings().updateCheckIntervalSeconds,
|
|
408074
|
+
targets: {
|
|
408075
|
+
listNodes: () => agentRegistryService.listNodeLiveness(),
|
|
408076
|
+
checkHubAddons: () => addonPackageService.checkUpdates(true),
|
|
408077
|
+
checkFrameworkPackages: () => addonPackageService.listFrameworkPackages(),
|
|
408078
|
+
checkAgentAddons: async (nodeId) => {
|
|
408079
|
+
const broker = moleculerService.broker;
|
|
408080
|
+
const installed = await (0, agent_installed_packages_js_1.fetchAgentInstalledPackages)(broker, nodeId);
|
|
408081
|
+
return addonPackageService.checkUpdatesForInstalled(installed, nodeId);
|
|
408082
|
+
},
|
|
408083
|
+
checkNodeServerUpdate: async (nodeId, isHub) => {
|
|
408084
|
+
if (isHub)
|
|
408085
|
+
return serverUpdateService.checkServerUpdate();
|
|
408086
|
+
const proxy = moleculerService.createCapabilityProxy("server-management", nodeId);
|
|
408087
|
+
if (proxy === null) {
|
|
408088
|
+
updateCheckLogger.warn("Update check skipped \u2014 server-management unreachable", {
|
|
408089
|
+
tags: { nodeId }
|
|
408090
|
+
});
|
|
408091
|
+
return void 0;
|
|
408092
|
+
}
|
|
408093
|
+
return proxy["checkServerUpdate"]?.({});
|
|
408094
|
+
}
|
|
408095
|
+
}
|
|
408096
|
+
});
|
|
408097
|
+
addonPackageService.setUpdateCheckRescheduler(() => updateCheckScheduler.reschedule());
|
|
408098
|
+
updateCheckScheduler.start();
|
|
406563
408099
|
const container = new ServiceContainer();
|
|
406564
408100
|
container.register(config_service_1.ConfigService, configService);
|
|
406565
408101
|
container.register(logging_service_1.LoggingService, loggingService);
|
|
@@ -406584,6 +408120,7 @@ var require_manual_boot = __commonJS({
|
|
|
406584
408120
|
container.register(server_update_service_1.ServerUpdateService, serverUpdateService);
|
|
406585
408121
|
container.register(topology_emitter_service_1.TopologyEmitterService, topologyEmitterService);
|
|
406586
408122
|
container.register(post_boot_service_1.PostBootService, postBootService);
|
|
408123
|
+
container.register(update_check_scheduler_js_1.UpdateCheckScheduler, updateCheckScheduler);
|
|
406587
408124
|
const fastify = (0, fastify_1.default)(fastifyOpts);
|
|
406588
408125
|
let closed = false;
|
|
406589
408126
|
let shutdownHooksRegistered = false;
|
|
@@ -406594,6 +408131,11 @@ var require_manual_boot = __commonJS({
|
|
|
406594
408131
|
const logErr = (label, err) => {
|
|
406595
408132
|
console.error(`[manual-boot] ${label} destroy failed:`, err);
|
|
406596
408133
|
};
|
|
408134
|
+
try {
|
|
408135
|
+
updateCheckScheduler.stop();
|
|
408136
|
+
} catch (err) {
|
|
408137
|
+
logErr("UpdateCheckScheduler", err);
|
|
408138
|
+
}
|
|
406597
408139
|
try {
|
|
406598
408140
|
topologyEmitterService.onModuleDestroy();
|
|
406599
408141
|
} catch (err) {
|