camstack 1.2.28 → 1.2.30
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-CB2eCKJb.js
|
|
23635
|
+
var require_dist_CB2eCKJb = __commonJS({
|
|
23636
|
+
"../system/dist/dist-CB2eCKJb.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,65 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
38823
38891
|
startMs: zod.z.number(),
|
|
38824
38892
|
endMs: zod.z.number()
|
|
38825
38893
|
}),
|
|
38826
|
-
/**
|
|
38827
|
-
|
|
38894
|
+
/**
|
|
38895
|
+
* Distinct object classes attached to this visit (`person`, `car`, …),
|
|
38896
|
+
* dominant first, capped at {@link MAX_CLIP_LABELS}. The ribbon renders these
|
|
38897
|
+
* instead of the bare kind — "Object" tells the operator nothing a colour bar
|
|
38898
|
+
* did not. Absent (never empty) when the visit attached no classified object
|
|
38899
|
+
* event, so a motion/audio-only visit keeps its kind label.
|
|
38900
|
+
*/
|
|
38901
|
+
labels: zod.z.array(zod.z.string()).max(3).optional(),
|
|
38902
|
+
/**
|
|
38903
|
+
* Lazy thumbnail URL, never inlined.
|
|
38904
|
+
*
|
|
38905
|
+
* Recording-derived clips (events-mode keep-window, and the prepared
|
|
38906
|
+
* continuous event+fragment visit) MUST use the snapshot of the **main
|
|
38907
|
+
* event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
|
|
38908
|
+
* of the event that owns `kind` (object > motion > audio). Do not extract
|
|
38909
|
+
* a keyframe from the recorded segments. Other providers (onboard, HKSV)
|
|
38910
|
+
* mint their own stills.
|
|
38911
|
+
*
|
|
38912
|
+
* **VOUCHED, never fabricated.** A provider emits this only for an event it
|
|
38913
|
+
* has CONFIRMED owns at least one media row (its own, or its owning track's).
|
|
38914
|
+
* Absent is meaningful — "this visit has no event still" — never "we did not
|
|
38915
|
+
* look". Stamping it from a URL template made 35% of one camera's clips point
|
|
38916
|
+
* at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
|
|
38917
|
+
* A read that FAILS drops the claim; it never invents it.
|
|
38918
|
+
*/
|
|
38919
|
+
thumbnail: zod.z.string().optional(),
|
|
38920
|
+
/**
|
|
38921
|
+
* An instant INSIDE this visit's footage, hole-safe, where a recorded still
|
|
38922
|
+
* can be decoded. Present whenever the visit came from recorded availability;
|
|
38923
|
+
* absent on a per-event padded window (there is no footage to promise).
|
|
38924
|
+
*
|
|
38925
|
+
* This is not a thumbnail and not a second byte path: it is the argument to
|
|
38926
|
+
* the recorder's existing still route. The surface — never the provider —
|
|
38927
|
+
* decides whether to use it. It is the midpoint of the visit's LONGEST
|
|
38928
|
+
* contiguous range, not of the visit: a visit spans its holes by
|
|
38929
|
+
* construction, so a naive midpoint lands in dead air.
|
|
38930
|
+
*/
|
|
38931
|
+
stillAtMs: zod.z.number().optional(),
|
|
38932
|
+
/**
|
|
38933
|
+
* Analytics event ids that overlap this visit — a BOUNDED sample, newest
|
|
38934
|
+
* first within kind (object → motion → audio), capped at
|
|
38935
|
+
* {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
|
|
38936
|
+
*
|
|
38937
|
+
* Bounded because it is not a payload the surface pages through: one visit on
|
|
38938
|
+
* device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
|
|
38939
|
+
* camera-day. Read {@link eventCount} for the true total.
|
|
38940
|
+
*/
|
|
38941
|
+
eventIds: zod.z.array(zod.z.string()).optional(),
|
|
38942
|
+
/** How many analytics events actually overlap this visit. Differs from
|
|
38943
|
+
* `eventIds.length` exactly when the sample was capped — so a truncated
|
|
38944
|
+
* list is never mistaken for a quiet visit. */
|
|
38945
|
+
eventCount: zod.z.number().int().nonnegative().optional(),
|
|
38946
|
+
/** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
|
|
38947
|
+
* than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
|
|
38948
|
+
* bar keeps showing them via `recording.getAvailability`. */
|
|
38949
|
+
holes: zod.z.array(zod.z.object({
|
|
38950
|
+
startMs: zod.z.number(),
|
|
38951
|
+
endMs: zod.z.number()
|
|
38952
|
+
})).optional()
|
|
38828
38953
|
});
|
|
38829
38954
|
var ClipPlaybackSchema = zod.z.object({
|
|
38830
38955
|
/** HLS master URL through the hub data-plane (Range + token in path). */
|
|
@@ -39372,7 +39497,14 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
39372
39497
|
});
|
|
39373
39498
|
var AutoUpdateSettingsSchema = zod.z.object({
|
|
39374
39499
|
channel: ChannelSchema,
|
|
39375
|
-
intervalSeconds: zod.z.number()
|
|
39500
|
+
intervalSeconds: zod.z.number(),
|
|
39501
|
+
/**
|
|
39502
|
+
* Cadence of the "an update exists" POLLER, in seconds. Independent of
|
|
39503
|
+
* `channel`: the poller runs while auto-apply is `off`, because being told
|
|
39504
|
+
* about a publish and installing it are different decisions. Clamped
|
|
39505
|
+
* server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
|
|
39506
|
+
*/
|
|
39507
|
+
updateCheckIntervalSeconds: zod.z.number()
|
|
39376
39508
|
});
|
|
39377
39509
|
var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
39378
39510
|
var RestartAddonResultSchema = zod.z.unknown();
|
|
@@ -39623,7 +39755,9 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
39623
39755
|
getAutoUpdateSettings: method(zod.z.void(), AutoUpdateSettingsSchema, { auth: "admin" }),
|
|
39624
39756
|
setAutoUpdateSettings: method(zod.z.object({
|
|
39625
39757
|
channel: ChannelSchema,
|
|
39626
|
-
intervalSeconds: zod.z.number().min(300).max(86400).optional()
|
|
39758
|
+
intervalSeconds: zod.z.number().min(300).max(86400).optional(),
|
|
39759
|
+
/** Availability-poll cadence; see AutoUpdateSettingsSchema. */
|
|
39760
|
+
updateCheckIntervalSeconds: zod.z.number().min(900).max(604800).optional()
|
|
39627
39761
|
}), zod.z.unknown(), {
|
|
39628
39762
|
kind: "mutation",
|
|
39629
39763
|
auth: "admin"
|
|
@@ -44905,7 +45039,9 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
44905
45039
|
includeAudio: zod.z.boolean(),
|
|
44906
45040
|
maxLifeMs: zod.z.number().int().positive(),
|
|
44907
45041
|
deleteAfterDownload: zod.z.boolean(),
|
|
44908
|
-
title: zod.z.string().max(200).optional()
|
|
45042
|
+
title: zod.z.string().max(200).optional(),
|
|
45043
|
+
/** Notification-output target ids to ping when this export becomes ready. */
|
|
45044
|
+
notifyTargetIds: zod.z.array(zod.z.string().min(1)).max(20).optional()
|
|
44909
45045
|
}).superRefine((v, ctx) => {
|
|
44910
45046
|
if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
|
|
44911
45047
|
code: zod.z.ZodIssueCode.custom,
|
|
@@ -44959,14 +45095,23 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
44959
45095
|
scope: "system",
|
|
44960
45096
|
mode: "singleton",
|
|
44961
45097
|
methods: {
|
|
44962
|
-
/** Queue a render of `[fromMs,toMs)` for `deviceId`/`
|
|
44963
|
-
*
|
|
45098
|
+
/** Queue a render of `[fromMs,toMs)` for `deviceId`/`profiles` (legacy
|
|
45099
|
+
* singular `profile` still accepted). Fails fast when no footage covers
|
|
45100
|
+
* the range. One job per profile; returns the first queued record. */
|
|
44964
45101
|
createExport: method(zod.z.object({
|
|
44965
45102
|
deviceId: zod.z.number(),
|
|
44966
|
-
|
|
45103
|
+
/** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
|
|
45104
|
+
profile: zod.z.string().optional(),
|
|
45105
|
+
profiles: zod.z.array(zod.z.string()).min(1).optional(),
|
|
44967
45106
|
fromMs: zod.z.number(),
|
|
44968
45107
|
toMs: zod.z.number(),
|
|
44969
45108
|
options: ExportOptionsSchema
|
|
45109
|
+
}).superRefine((v, ctx) => {
|
|
45110
|
+
if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
|
|
45111
|
+
code: zod.z.ZodIssueCode.custom,
|
|
45112
|
+
message: "pass profiles[] (min 1) or legacy profile",
|
|
45113
|
+
path: ["profiles"]
|
|
45114
|
+
});
|
|
44970
45115
|
}), ExportRecordSchema, {
|
|
44971
45116
|
kind: "mutation",
|
|
44972
45117
|
auth: "protected"
|
|
@@ -50603,6 +50748,12 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
50603
50748
|
addonId: null,
|
|
50604
50749
|
access: "create"
|
|
50605
50750
|
},
|
|
50751
|
+
"pipelineAnalytics.reconcileFromDisk": {
|
|
50752
|
+
capName: "pipeline-analytics",
|
|
50753
|
+
capScope: "device",
|
|
50754
|
+
addonId: null,
|
|
50755
|
+
access: "create"
|
|
50756
|
+
},
|
|
50606
50757
|
"pipelineAnalytics.refreshStorageLocationsForMigration": {
|
|
50607
50758
|
capName: "pipeline-analytics",
|
|
50608
50759
|
capScope: "device",
|
|
@@ -51005,6 +51156,12 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
51005
51156
|
addonId: null,
|
|
51006
51157
|
access: "view"
|
|
51007
51158
|
},
|
|
51159
|
+
"pipelineOrchestrator.getReconcileFromDiskStatus": {
|
|
51160
|
+
capName: "pipeline-orchestrator",
|
|
51161
|
+
capScope: "system",
|
|
51162
|
+
addonId: null,
|
|
51163
|
+
access: "view"
|
|
51164
|
+
},
|
|
51008
51165
|
"pipelineOrchestrator.listAgentSettings": {
|
|
51009
51166
|
capName: "pipeline-orchestrator",
|
|
51010
51167
|
capScope: "system",
|
|
@@ -51029,6 +51186,12 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
51029
51186
|
addonId: null,
|
|
51030
51187
|
access: "create"
|
|
51031
51188
|
},
|
|
51189
|
+
"pipelineOrchestrator.reconcileFromDisk": {
|
|
51190
|
+
capName: "pipeline-orchestrator",
|
|
51191
|
+
capScope: "system",
|
|
51192
|
+
addonId: null,
|
|
51193
|
+
access: "create"
|
|
51194
|
+
},
|
|
51032
51195
|
"pipelineOrchestrator.removeAgentSettings": {
|
|
51033
51196
|
capName: "pipeline-orchestrator",
|
|
51034
51197
|
capScope: "system",
|
|
@@ -54020,6 +54183,11 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
54020
54183
|
form: "single",
|
|
54021
54184
|
optional: true
|
|
54022
54185
|
}],
|
|
54186
|
+
"pipelineAnalytics.reconcileFromDisk": [{
|
|
54187
|
+
name: "deviceId",
|
|
54188
|
+
form: "single",
|
|
54189
|
+
optional: false
|
|
54190
|
+
}],
|
|
54023
54191
|
"pipelineAnalytics.restageRetrainTrack": [{
|
|
54024
54192
|
name: "deviceId",
|
|
54025
54193
|
form: "single",
|
|
@@ -55207,6 +55375,8 @@ var require_dist_tMrPAfpF = __commonJS({
|
|
|
55207
55375
|
function isSameAddonId(a, b) {
|
|
55208
55376
|
return bareAddonId(a) === bareAddonId(b);
|
|
55209
55377
|
}
|
|
55378
|
+
var MB = 1024 * 1024;
|
|
55379
|
+
1024 * MB, 3072 * MB;
|
|
55210
55380
|
function decodeVectorBase64(encoded) {
|
|
55211
55381
|
const buffer = Buffer.from(encoded, "base64");
|
|
55212
55382
|
if (buffer.byteLength % 4 !== 0) throw new Error(`decodeVectorBase64: ${buffer.byteLength} bytes is not a whole number of Float32 values`);
|
|
@@ -55913,7 +56083,7 @@ var require_alerts_addon = __commonJS({
|
|
|
55913
56083
|
[Symbol.toStringTag]: { value: "Module" }
|
|
55914
56084
|
});
|
|
55915
56085
|
require_chunk_Cek0wNdY();
|
|
55916
|
-
var require_dist10 =
|
|
56086
|
+
var require_dist10 = require_dist_CB2eCKJb();
|
|
55917
56087
|
function selectExpired(alerts, cutoffMs) {
|
|
55918
56088
|
return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
|
|
55919
56089
|
}
|
|
@@ -56726,7 +56896,7 @@ var require_console_logging = __commonJS({
|
|
|
56726
56896
|
[Symbol.toStringTag]: { value: "Module" }
|
|
56727
56897
|
});
|
|
56728
56898
|
require_chunk_Cek0wNdY();
|
|
56729
|
-
var require_dist10 =
|
|
56899
|
+
var require_dist10 = require_dist_CB2eCKJb();
|
|
56730
56900
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
56731
56901
|
var LEVEL_RANK = {
|
|
56732
56902
|
debug: 0,
|
|
@@ -56820,7 +56990,7 @@ var require_core_blocks_addon = __commonJS({
|
|
|
56820
56990
|
"use strict";
|
|
56821
56991
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
56822
56992
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
56823
|
-
var require_dist10 =
|
|
56993
|
+
var require_dist10 = require_dist_CB2eCKJb();
|
|
56824
56994
|
var node_crypto = __require("crypto");
|
|
56825
56995
|
var node_fs_promises = __require("fs/promises");
|
|
56826
56996
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -59540,7 +59710,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
59540
59710
|
[Symbol.toStringTag]: { value: "Module" }
|
|
59541
59711
|
});
|
|
59542
59712
|
require_chunk_Cek0wNdY();
|
|
59543
|
-
var require_dist10 =
|
|
59713
|
+
var require_dist10 = require_dist_CB2eCKJb();
|
|
59544
59714
|
var node_crypto = __require("crypto");
|
|
59545
59715
|
var _camstack_types_node = require_node();
|
|
59546
59716
|
var JOB_HISTORY = 20;
|
|
@@ -63528,7 +63698,7 @@ var require_hub_forwarder = __commonJS({
|
|
|
63528
63698
|
[Symbol.toStringTag]: { value: "Module" }
|
|
63529
63699
|
});
|
|
63530
63700
|
require_chunk_Cek0wNdY();
|
|
63531
|
-
var require_dist10 =
|
|
63701
|
+
var require_dist10 = require_dist_CB2eCKJb();
|
|
63532
63702
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
63533
63703
|
var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
|
|
63534
63704
|
var HubForwarderDestination = class {
|
|
@@ -63665,7 +63835,7 @@ var require_liveness_monitor_addon = __commonJS({
|
|
|
63665
63835
|
"use strict";
|
|
63666
63836
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
63667
63837
|
require_chunk_Cek0wNdY();
|
|
63668
|
-
var require_dist10 =
|
|
63838
|
+
var require_dist10 = require_dist_CB2eCKJb();
|
|
63669
63839
|
var NO_DEVICES = "liveness:no-devices";
|
|
63670
63840
|
var ALL_OFFLINE = "liveness:all-devices-offline";
|
|
63671
63841
|
var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
|
|
@@ -63855,7 +64025,7 @@ var require_local_auth_addon = __commonJS({
|
|
|
63855
64025
|
[Symbol.toStringTag]: { value: "Module" }
|
|
63856
64026
|
});
|
|
63857
64027
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
63858
|
-
var require_dist10 =
|
|
64028
|
+
var require_dist10 = require_dist_CB2eCKJb();
|
|
63859
64029
|
var node_crypto = __require("crypto");
|
|
63860
64030
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
63861
64031
|
var crypto$1 = __require("crypto");
|
|
@@ -71539,7 +71709,7 @@ var require_loki_logging = __commonJS({
|
|
|
71539
71709
|
[Symbol.toStringTag]: { value: "Module" }
|
|
71540
71710
|
});
|
|
71541
71711
|
require_chunk_Cek0wNdY();
|
|
71542
|
-
var require_dist10 =
|
|
71712
|
+
var require_dist10 = require_dist_CB2eCKJb();
|
|
71543
71713
|
function sanitizeLabelName(raw) {
|
|
71544
71714
|
const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
|
|
71545
71715
|
return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
|
|
@@ -72104,7 +72274,7 @@ var require_native_metrics_addon = __commonJS({
|
|
|
72104
72274
|
[Symbol.toStringTag]: { value: "Module" }
|
|
72105
72275
|
});
|
|
72106
72276
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
72107
|
-
var require_dist10 =
|
|
72277
|
+
var require_dist10 = require_dist_CB2eCKJb();
|
|
72108
72278
|
var node_child_process = __require("child_process");
|
|
72109
72279
|
var node_util = __require("util");
|
|
72110
72280
|
var node_os = __require("os");
|
|
@@ -73046,7 +73216,7 @@ var require_filesystem_storage_addon = __commonJS({
|
|
|
73046
73216
|
[Symbol.toStringTag]: { value: "Module" }
|
|
73047
73217
|
});
|
|
73048
73218
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
73049
|
-
var require_dist10 =
|
|
73219
|
+
var require_dist10 = require_dist_CB2eCKJb();
|
|
73050
73220
|
var node_crypto = __require("crypto");
|
|
73051
73221
|
var node_fs_promises = __require("fs/promises");
|
|
73052
73222
|
var node_path = __require("path");
|
|
@@ -74162,7 +74332,7 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74162
74332
|
[Symbol.toStringTag]: { value: "Module" }
|
|
74163
74333
|
});
|
|
74164
74334
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
74165
|
-
var require_dist10 =
|
|
74335
|
+
var require_dist10 = require_dist_CB2eCKJb();
|
|
74166
74336
|
var node_crypto = __require("crypto");
|
|
74167
74337
|
var node_fs = __require("fs");
|
|
74168
74338
|
var node_module = __require("module");
|
|
@@ -75730,7 +75900,7 @@ var require_storage_orchestrator_addon = __commonJS({
|
|
|
75730
75900
|
[Symbol.toStringTag]: { value: "Module" }
|
|
75731
75901
|
});
|
|
75732
75902
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
75733
|
-
var require_dist10 =
|
|
75903
|
+
var require_dist10 = require_dist_CB2eCKJb();
|
|
75734
75904
|
var node_crypto = __require("crypto");
|
|
75735
75905
|
var node_fs_promises = __require("fs/promises");
|
|
75736
75906
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -77609,7 +77779,7 @@ var require_system_config_addon = __commonJS({
|
|
|
77609
77779
|
[Symbol.toStringTag]: { value: "Module" }
|
|
77610
77780
|
});
|
|
77611
77781
|
require_chunk_Cek0wNdY();
|
|
77612
|
-
var require_dist10 =
|
|
77782
|
+
var require_dist10 = require_dist_CB2eCKJb();
|
|
77613
77783
|
var SECTION_TITLES = {
|
|
77614
77784
|
server: "Server",
|
|
77615
77785
|
auth: "Authentication"
|
|
@@ -95670,7 +95840,7 @@ var require_winston_logging = __commonJS({
|
|
|
95670
95840
|
[Symbol.toStringTag]: { value: "Module" }
|
|
95671
95841
|
});
|
|
95672
95842
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
95673
|
-
var require_dist10 =
|
|
95843
|
+
var require_dist10 = require_dist_CB2eCKJb();
|
|
95674
95844
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
95675
95845
|
var node_path = __require("path");
|
|
95676
95846
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -96658,9 +96828,9 @@ var require_file_data_plane_DUHPHa_Y = __commonJS({
|
|
|
96658
96828
|
}
|
|
96659
96829
|
});
|
|
96660
96830
|
|
|
96661
|
-
// ../types/dist/event-category-
|
|
96662
|
-
var
|
|
96663
|
-
"../types/dist/event-category-
|
|
96831
|
+
// ../types/dist/event-category-CRPORAAz.js
|
|
96832
|
+
var require_event_category_CRPORAAz = __commonJS({
|
|
96833
|
+
"../types/dist/event-category-CRPORAAz.js"(exports) {
|
|
96664
96834
|
"use strict";
|
|
96665
96835
|
var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
|
|
96666
96836
|
EventCategory2["SystemBoot"] = "system.boot";
|
|
@@ -96676,6 +96846,7 @@ var require_event_category_D3gG7oil = __commonJS({
|
|
|
96676
96846
|
EventCategory2["AddonInstalled"] = "addon.installed";
|
|
96677
96847
|
EventCategory2["AddonUninstalled"] = "addon.uninstalled";
|
|
96678
96848
|
EventCategory2["AddonCrashed"] = "addon.crashed";
|
|
96849
|
+
EventCategory2["AddonRunnerFailed"] = "addon.runner-failed";
|
|
96679
96850
|
EventCategory2["AddonError"] = "addon.error";
|
|
96680
96851
|
EventCategory2["AddonPageReady"] = "addon.page-ready";
|
|
96681
96852
|
EventCategory2["AddonWidgetReady"] = "addon.widget-ready";
|
|
@@ -96823,11 +96994,11 @@ var require_event_category_D3gG7oil = __commonJS({
|
|
|
96823
96994
|
}
|
|
96824
96995
|
});
|
|
96825
96996
|
|
|
96826
|
-
// ../types/dist/sleep-
|
|
96827
|
-
var
|
|
96828
|
-
"../types/dist/sleep-
|
|
96997
|
+
// ../types/dist/sleep-CMRLJj2e.js
|
|
96998
|
+
var require_sleep_CMRLJj2e = __commonJS({
|
|
96999
|
+
"../types/dist/sleep-CMRLJj2e.js"(exports) {
|
|
96829
97000
|
"use strict";
|
|
96830
|
-
var require_event_category =
|
|
97001
|
+
var require_event_category = require_event_category_CRPORAAz();
|
|
96831
97002
|
var zod = require_zod();
|
|
96832
97003
|
var WELL_KNOWN_TABS = [
|
|
96833
97004
|
{
|
|
@@ -99298,6 +99469,7 @@ var require_sleep_EYtyUX0L = __commonJS({
|
|
|
99298
99469
|
pruneEventsBefore: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "pruneEventsBefore", "mutation", input),
|
|
99299
99470
|
pruneTracksBefore: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "pruneTracksBefore", "mutation", input),
|
|
99300
99471
|
wipeAllAnalytics: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "wipeAllAnalytics", "mutation", input),
|
|
99472
|
+
reconcileFromDisk: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "reconcileFromDisk", "mutation", input),
|
|
99301
99473
|
deleteTracks: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "deleteTracks", "mutation", input),
|
|
99302
99474
|
setTrackFlags: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "setTrackFlags", "mutation", input),
|
|
99303
99475
|
getEventStoreFootprint: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getEventStoreFootprint", "query", input),
|
|
@@ -99600,10 +99772,7 @@ var require_sleep_EYtyUX0L = __commonJS({
|
|
|
99600
99772
|
getDeviceLiveContribution: (input) => dispatchSystem("recording", "getDeviceLiveContribution", "query", input),
|
|
99601
99773
|
applyDeviceSettingsPatch: (input) => dispatchSystem("recording", "applyDeviceSettingsPatch", "mutation", input)
|
|
99602
99774
|
},
|
|
99603
|
-
recordingExport: {
|
|
99604
|
-
createExport: (input) => dispatchSystem("recordingExport", "createExport", "mutation", input),
|
|
99605
|
-
listExports: (input) => dispatchSystem("recordingExport", "listExports", "query", input)
|
|
99606
|
-
},
|
|
99775
|
+
recordingExport: { listExports: (input) => dispatchSystem("recordingExport", "listExports", "query", input) },
|
|
99607
99776
|
streamBroker: {
|
|
99608
99777
|
publishCameraStream: (input) => dispatchSystem("streamBroker", "publishCameraStream", "mutation", input),
|
|
99609
99778
|
retractCameraStream: (input) => dispatchSystem("streamBroker", "retractCameraStream", "mutation", input),
|
|
@@ -100266,8 +100435,8 @@ var require_addon = __commonJS({
|
|
|
100266
100435
|
"../types/dist/addon.js"(exports) {
|
|
100267
100436
|
"use strict";
|
|
100268
100437
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
100269
|
-
var require_event_category =
|
|
100270
|
-
var require_sleep =
|
|
100438
|
+
var require_event_category = require_event_category_CRPORAAz();
|
|
100439
|
+
var require_sleep = require_sleep_CMRLJj2e();
|
|
100271
100440
|
var require_err_msg = require_err_msg_COpsHMw2();
|
|
100272
100441
|
var CAP_INPUT_DEFAULTS = Object.freeze({
|
|
100273
100442
|
"addons": { "getLogs": { "limit": 100 } },
|
|
@@ -107141,9 +107310,9 @@ var require_dist2 = __commonJS({
|
|
|
107141
107310
|
}
|
|
107142
107311
|
});
|
|
107143
107312
|
|
|
107144
|
-
// ../system/dist/manifest-python-deps-
|
|
107145
|
-
var
|
|
107146
|
-
"../system/dist/manifest-python-deps-
|
|
107313
|
+
// ../system/dist/manifest-python-deps-GejnH--L.js
|
|
107314
|
+
var require_manifest_python_deps_GejnH_L = __commonJS({
|
|
107315
|
+
"../system/dist/manifest-python-deps-GejnH--L.js"(exports) {
|
|
107147
107316
|
"use strict";
|
|
107148
107317
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
107149
107318
|
var node_crypto = __require("crypto");
|
|
@@ -107159,9 +107328,139 @@ var require_manifest_python_deps_BtVjvfvj = __commonJS({
|
|
|
107159
107328
|
var node_fs = __require("fs");
|
|
107160
107329
|
node_fs = require_chunk.__toESM(node_fs);
|
|
107161
107330
|
var node_http = __require("http");
|
|
107331
|
+
var node_v8 = __require("v8");
|
|
107332
|
+
node_v8 = require_chunk.__toESM(node_v8);
|
|
107333
|
+
var node_vm = __require("vm");
|
|
107334
|
+
node_vm = require_chunk.__toESM(node_vm);
|
|
107162
107335
|
var _camstack_types_addon = require_addon();
|
|
107163
107336
|
var _trpc_client = require_dist2();
|
|
107164
107337
|
var node_net = __require("net");
|
|
107338
|
+
var HEAP_WATCH_INTERVAL_MS = 6e4;
|
|
107339
|
+
var HEAP_WATCH_WARN_RATIO = 0.8;
|
|
107340
|
+
var HEAP_WATCH_ESCALATE_RATIO = 0.7;
|
|
107341
|
+
var HEAP_WATCH_DEESCALATE_RATIO = 0.6;
|
|
107342
|
+
function nextMode(current, usedRatio, escalateRatio = HEAP_WATCH_ESCALATE_RATIO, deescalateRatio = HEAP_WATCH_DEESCALATE_RATIO) {
|
|
107343
|
+
if (current === "escalated") return usedRatio < deescalateRatio ? "steady" : "escalated";
|
|
107344
|
+
return usedRatio >= escalateRatio ? "escalated" : "steady";
|
|
107345
|
+
}
|
|
107346
|
+
var HEAP_RECLAIM_TRIGGER_MB = 1024;
|
|
107347
|
+
var HEAP_RECLAIM_MIN_INTERVAL_MS = 12e4;
|
|
107348
|
+
var MB = (bytes) => Math.round(bytes / 1048576);
|
|
107349
|
+
function buildHeapSample(mem, heapLimitBytes, warnRatio = HEAP_WATCH_WARN_RATIO) {
|
|
107350
|
+
const usedRatio = heapLimitBytes > 0 ? mem.heapUsed / heapLimitBytes : 0;
|
|
107351
|
+
return {
|
|
107352
|
+
rssMb: MB(mem.rss),
|
|
107353
|
+
heapUsedMb: MB(mem.heapUsed),
|
|
107354
|
+
heapTotalMb: MB(mem.heapTotal),
|
|
107355
|
+
heapLimitMb: MB(heapLimitBytes),
|
|
107356
|
+
externalMb: MB(mem.external),
|
|
107357
|
+
arrayBuffersMb: MB(mem.arrayBuffers),
|
|
107358
|
+
usedRatio: Math.round(usedRatio * 100) / 100,
|
|
107359
|
+
nearLimit: usedRatio >= warnRatio
|
|
107360
|
+
};
|
|
107361
|
+
}
|
|
107362
|
+
var consoleSink = {
|
|
107363
|
+
info: (line) => console.log(line),
|
|
107364
|
+
warn: (line) => console.warn(line)
|
|
107365
|
+
};
|
|
107366
|
+
function strandedMb(s) {
|
|
107367
|
+
return s.rssMb - s.heapUsedMb - s.externalMb;
|
|
107368
|
+
}
|
|
107369
|
+
function shouldReclaim(s, triggerMb = HEAP_RECLAIM_TRIGGER_MB) {
|
|
107370
|
+
return strandedMb(s) > triggerMb;
|
|
107371
|
+
}
|
|
107372
|
+
function isGcFunction(value) {
|
|
107373
|
+
return typeof value === "function";
|
|
107374
|
+
}
|
|
107375
|
+
function createV8Reclaimer() {
|
|
107376
|
+
try {
|
|
107377
|
+
node_v8.setFlagsFromString("--expose-gc");
|
|
107378
|
+
const gc = node_vm.runInNewContext("gc");
|
|
107379
|
+
node_v8.setFlagsFromString("--no-expose-gc");
|
|
107380
|
+
if (!isGcFunction(gc)) return void 0;
|
|
107381
|
+
return () => gc({
|
|
107382
|
+
execution: "sync",
|
|
107383
|
+
flavor: "last-resort"
|
|
107384
|
+
});
|
|
107385
|
+
} catch {
|
|
107386
|
+
return;
|
|
107387
|
+
}
|
|
107388
|
+
}
|
|
107389
|
+
function format2(label, s) {
|
|
107390
|
+
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`;
|
|
107391
|
+
}
|
|
107392
|
+
function startHeapWatch(label = "hub-main", sink = consoleSink, intervalMs = HEAP_WATCH_INTERVAL_MS, reclaimOptions) {
|
|
107393
|
+
const readMemory = reclaimOptions?.readMemory ?? (() => process.memoryUsage());
|
|
107394
|
+
const now = reclaimOptions?.now ?? (() => Date.now());
|
|
107395
|
+
const triggerMb = reclaimOptions?.triggerMb ?? 1024;
|
|
107396
|
+
const minIntervalMs = reclaimOptions?.minIntervalMs ?? 12e4;
|
|
107397
|
+
const fastIntervalMs = reclaimOptions?.fastIntervalMs ?? 2e3;
|
|
107398
|
+
const escalateRatio = reclaimOptions?.escalateRatio ?? 0.7;
|
|
107399
|
+
const deescalateRatio = reclaimOptions?.deescalateRatio ?? 0.6;
|
|
107400
|
+
let lastReclaimAt = Number.NEGATIVE_INFINITY;
|
|
107401
|
+
const read = () => {
|
|
107402
|
+
const limit = reclaimOptions?.heapLimitBytes ?? node_v8.getHeapStatistics().heap_size_limit;
|
|
107403
|
+
return buildHeapSample(readMemory(), limit);
|
|
107404
|
+
};
|
|
107405
|
+
const maybeReclaim = (sample) => {
|
|
107406
|
+
if (reclaimOptions === void 0) return;
|
|
107407
|
+
if (!shouldReclaim(sample, triggerMb)) return;
|
|
107408
|
+
if (now() - lastReclaimAt < minIntervalMs) return;
|
|
107409
|
+
lastReclaimAt = now();
|
|
107410
|
+
const startedAt = now();
|
|
107411
|
+
try {
|
|
107412
|
+
reclaimOptions.reclaim();
|
|
107413
|
+
} catch (error) {
|
|
107414
|
+
sink.warn(`[mem] reclaim failed \u2014 ${error instanceof Error ? error.message : String(error)}`);
|
|
107415
|
+
return;
|
|
107416
|
+
}
|
|
107417
|
+
const after = read();
|
|
107418
|
+
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`);
|
|
107419
|
+
};
|
|
107420
|
+
let mode = "steady";
|
|
107421
|
+
let lastLoggedAt = Number.NEGATIVE_INFINITY;
|
|
107422
|
+
const probeIntervalMs = Math.min(fastIntervalMs, intervalMs);
|
|
107423
|
+
const tick = () => {
|
|
107424
|
+
try {
|
|
107425
|
+
const sample = read();
|
|
107426
|
+
const previous = mode;
|
|
107427
|
+
mode = nextMode(previous, sample.usedRatio, escalateRatio, deescalateRatio);
|
|
107428
|
+
const at = now();
|
|
107429
|
+
const due = at - lastLoggedAt >= intervalMs;
|
|
107430
|
+
if (mode === "escalated" || due) {
|
|
107431
|
+
lastLoggedAt = at;
|
|
107432
|
+
const line = format2(label, sample);
|
|
107433
|
+
if (sample.nearLimit) sink.warn(`${line} \u2014 APPROACHING HEAP LIMIT`);
|
|
107434
|
+
else if (mode === "escalated") sink.warn(`${line} \u2014 heap elevated, sampling every ${probeIntervalMs}ms`);
|
|
107435
|
+
else sink.info(line);
|
|
107436
|
+
}
|
|
107437
|
+
if (previous === "escalated" && mode === "steady") sink.info(`[mem] ${label} heap back to routine \u2014 logging every ${intervalMs}ms`);
|
|
107438
|
+
maybeReclaim(sample);
|
|
107439
|
+
} catch {
|
|
107440
|
+
}
|
|
107441
|
+
};
|
|
107442
|
+
const timer = setInterval(tick, probeIntervalMs);
|
|
107443
|
+
timer.unref?.();
|
|
107444
|
+
tick();
|
|
107445
|
+
let stopped = false;
|
|
107446
|
+
return () => {
|
|
107447
|
+
if (stopped) return;
|
|
107448
|
+
stopped = true;
|
|
107449
|
+
clearInterval(timer);
|
|
107450
|
+
};
|
|
107451
|
+
}
|
|
107452
|
+
var RUNNER_HEAP_WATCH_INTERVAL_MS = 3e5;
|
|
107453
|
+
function startRunnerHeapWatch(options) {
|
|
107454
|
+
if (options.heapProfile !== "heavy") return void 0;
|
|
107455
|
+
const intervalMs = options.intervalMs ?? 3e5;
|
|
107456
|
+
if (options.reclaimSwitch === "off") return startHeapWatch(options.label, options.sink, intervalMs);
|
|
107457
|
+
let reclaimOptions = options.reclaimOptions;
|
|
107458
|
+
if (reclaimOptions === void 0) {
|
|
107459
|
+
const reclaimer = createV8Reclaimer();
|
|
107460
|
+
reclaimOptions = reclaimer === void 0 ? void 0 : { reclaim: reclaimer };
|
|
107461
|
+
}
|
|
107462
|
+
return startHeapWatch(options.label, options.sink, intervalMs, reclaimOptions);
|
|
107463
|
+
}
|
|
107165
107464
|
function trimSlashes(s) {
|
|
107166
107465
|
return s.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
107167
107466
|
}
|
|
@@ -113520,6 +113819,30 @@ var require_manifest_python_deps_BtVjvfvj = __commonJS({
|
|
|
113520
113819
|
return FrameDecoder;
|
|
113521
113820
|
}
|
|
113522
113821
|
});
|
|
113822
|
+
Object.defineProperty(exports, "HEAP_RECLAIM_MIN_INTERVAL_MS", {
|
|
113823
|
+
enumerable: true,
|
|
113824
|
+
get: function() {
|
|
113825
|
+
return HEAP_RECLAIM_MIN_INTERVAL_MS;
|
|
113826
|
+
}
|
|
113827
|
+
});
|
|
113828
|
+
Object.defineProperty(exports, "HEAP_RECLAIM_TRIGGER_MB", {
|
|
113829
|
+
enumerable: true,
|
|
113830
|
+
get: function() {
|
|
113831
|
+
return HEAP_RECLAIM_TRIGGER_MB;
|
|
113832
|
+
}
|
|
113833
|
+
});
|
|
113834
|
+
Object.defineProperty(exports, "HEAP_WATCH_INTERVAL_MS", {
|
|
113835
|
+
enumerable: true,
|
|
113836
|
+
get: function() {
|
|
113837
|
+
return HEAP_WATCH_INTERVAL_MS;
|
|
113838
|
+
}
|
|
113839
|
+
});
|
|
113840
|
+
Object.defineProperty(exports, "HEAP_WATCH_WARN_RATIO", {
|
|
113841
|
+
enumerable: true,
|
|
113842
|
+
get: function() {
|
|
113843
|
+
return HEAP_WATCH_WARN_RATIO;
|
|
113844
|
+
}
|
|
113845
|
+
});
|
|
113523
113846
|
Object.defineProperty(exports, "HUB_CAP_FWD_ACTION", {
|
|
113524
113847
|
enumerable: true,
|
|
113525
113848
|
get: function() {
|
|
@@ -113550,6 +113873,12 @@ var require_manifest_python_deps_BtVjvfvj = __commonJS({
|
|
|
113550
113873
|
return NATIVE_PROVIDER_SERVICE_INFIX;
|
|
113551
113874
|
}
|
|
113552
113875
|
});
|
|
113876
|
+
Object.defineProperty(exports, "RUNNER_HEAP_WATCH_INTERVAL_MS", {
|
|
113877
|
+
enumerable: true,
|
|
113878
|
+
get: function() {
|
|
113879
|
+
return RUNNER_HEAP_WATCH_INTERVAL_MS;
|
|
113880
|
+
}
|
|
113881
|
+
});
|
|
113553
113882
|
Object.defineProperty(exports, "SocketChannel", {
|
|
113554
113883
|
enumerable: true,
|
|
113555
113884
|
get: function() {
|
|
@@ -113598,6 +113927,12 @@ var require_manifest_python_deps_BtVjvfvj = __commonJS({
|
|
|
113598
113927
|
return brokerTransportLink;
|
|
113599
113928
|
}
|
|
113600
113929
|
});
|
|
113930
|
+
Object.defineProperty(exports, "buildHeapSample", {
|
|
113931
|
+
enumerable: true,
|
|
113932
|
+
get: function() {
|
|
113933
|
+
return buildHeapSample;
|
|
113934
|
+
}
|
|
113935
|
+
});
|
|
113601
113936
|
Object.defineProperty(exports, "buildLinkChain", {
|
|
113602
113937
|
enumerable: true,
|
|
113603
113938
|
get: function() {
|
|
@@ -113742,6 +114077,12 @@ var require_manifest_python_deps_BtVjvfvj = __commonJS({
|
|
|
113742
114077
|
return createUdsLoggerWithControl;
|
|
113743
114078
|
}
|
|
113744
114079
|
});
|
|
114080
|
+
Object.defineProperty(exports, "createV8Reclaimer", {
|
|
114081
|
+
enumerable: true,
|
|
114082
|
+
get: function() {
|
|
114083
|
+
return createV8Reclaimer;
|
|
114084
|
+
}
|
|
114085
|
+
});
|
|
113745
114086
|
Object.defineProperty(exports, "deserializeTypedArrays", {
|
|
113746
114087
|
enumerable: true,
|
|
113747
114088
|
get: function() {
|
|
@@ -113904,6 +114245,30 @@ var require_manifest_python_deps_BtVjvfvj = __commonJS({
|
|
|
113904
114245
|
return setWorkerNativeCapsChangeListener;
|
|
113905
114246
|
}
|
|
113906
114247
|
});
|
|
114248
|
+
Object.defineProperty(exports, "shouldReclaim", {
|
|
114249
|
+
enumerable: true,
|
|
114250
|
+
get: function() {
|
|
114251
|
+
return shouldReclaim;
|
|
114252
|
+
}
|
|
114253
|
+
});
|
|
114254
|
+
Object.defineProperty(exports, "startHeapWatch", {
|
|
114255
|
+
enumerable: true,
|
|
114256
|
+
get: function() {
|
|
114257
|
+
return startHeapWatch;
|
|
114258
|
+
}
|
|
114259
|
+
});
|
|
114260
|
+
Object.defineProperty(exports, "startRunnerHeapWatch", {
|
|
114261
|
+
enumerable: true,
|
|
114262
|
+
get: function() {
|
|
114263
|
+
return startRunnerHeapWatch;
|
|
114264
|
+
}
|
|
114265
|
+
});
|
|
114266
|
+
Object.defineProperty(exports, "strandedMb", {
|
|
114267
|
+
enumerable: true,
|
|
114268
|
+
get: function() {
|
|
114269
|
+
return strandedMb;
|
|
114270
|
+
}
|
|
114271
|
+
});
|
|
113907
114272
|
Object.defineProperty(exports, "subscribePassthrough", {
|
|
113908
114273
|
enumerable: true,
|
|
113909
114274
|
get: function() {
|
|
@@ -117724,7 +118089,7 @@ var require_dist3 = __commonJS({
|
|
|
117724
118089
|
"use strict";
|
|
117725
118090
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
117726
118091
|
var require_chunk = require_chunk_Cek0wNdY();
|
|
117727
|
-
var require_dist10 =
|
|
118092
|
+
var require_dist10 = require_dist_CB2eCKJb();
|
|
117728
118093
|
var require_builtins_alerts_alerts_addon = require_alerts_addon();
|
|
117729
118094
|
require_alerts();
|
|
117730
118095
|
var require_formatter = require_formatter_DqAKDlvN();
|
|
@@ -117749,7 +118114,7 @@ var require_dist3 = __commonJS({
|
|
|
117749
118114
|
require_system_config();
|
|
117750
118115
|
var require_builtins_winston_logging_index = require_winston_logging();
|
|
117751
118116
|
var require_file_data_plane = require_file_data_plane_DUHPHa_Y();
|
|
117752
|
-
var require_manifest_python_deps =
|
|
118117
|
+
var require_manifest_python_deps = require_manifest_python_deps_GejnH_L();
|
|
117753
118118
|
var require_resource_monitor = require_resource_monitor_CdnzxBLP();
|
|
117754
118119
|
var require_custom_action_registry = require_custom_action_registry_jY0NOZK8();
|
|
117755
118120
|
var zod = require_zod();
|
|
@@ -117767,8 +118132,6 @@ var require_dist3 = __commonJS({
|
|
|
117767
118132
|
var node_fs$1 = require_chunk.__toESM(node_fs, 1);
|
|
117768
118133
|
node_fs = require_chunk.__toESM(node_fs);
|
|
117769
118134
|
var node_http = __require("http");
|
|
117770
|
-
var node_v8 = __require("v8");
|
|
117771
|
-
node_v8 = require_chunk.__toESM(node_v8);
|
|
117772
118135
|
var node_vm = __require("vm");
|
|
117773
118136
|
node_vm = require_chunk.__toESM(node_vm);
|
|
117774
118137
|
var _camstack_types_addon = require_addon();
|
|
@@ -118102,120 +118465,6 @@ var require_dist3 = __commonJS({
|
|
|
118102
118465
|
if (replayBody !== void 0) upstream.end(replayBody);
|
|
118103
118466
|
else clientReq.pipe(upstream);
|
|
118104
118467
|
}
|
|
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
118468
|
var VALID_TRANSITIONS = {
|
|
118220
118469
|
stopped: ["starting", "disabled"],
|
|
118221
118470
|
starting: [
|
|
@@ -118447,17 +118696,27 @@ var require_dist3 = __commonJS({
|
|
|
118447
118696
|
return bare;
|
|
118448
118697
|
}
|
|
118449
118698
|
var PRUNE_TARGET_RATIO = 0.9;
|
|
118699
|
+
var DEFAULT_MAX_TOTAL_LOG_ENTRIES = 5e4;
|
|
118450
118700
|
var PartitionedLogBuffer = class {
|
|
118451
118701
|
buffers = /* @__PURE__ */ new Map();
|
|
118702
|
+
/**
|
|
118703
|
+
* Bucket key → the write ordinal of its last `push`. A monotonic counter, not
|
|
118704
|
+
* a clock: eviction order must follow the order writes actually happened, and
|
|
118705
|
+
* a wall clock that steps backwards would pick the wrong victim.
|
|
118706
|
+
*/
|
|
118707
|
+
lastWriteSeq = /* @__PURE__ */ new Map();
|
|
118708
|
+
writeSeq = 0;
|
|
118452
118709
|
perAddonCapacity;
|
|
118453
118710
|
maxTotalEntries;
|
|
118454
118711
|
pruneLevel;
|
|
118712
|
+
maxBuckets;
|
|
118455
118713
|
/** Running total, so `push` does not walk every bucket to decide. */
|
|
118456
118714
|
totalEntries = 0;
|
|
118457
118715
|
constructor(perAddonCapacity = 5e3, options = {}) {
|
|
118458
118716
|
this.perAddonCapacity = options.perAddonCapacity ?? perAddonCapacity;
|
|
118459
|
-
this.maxTotalEntries = options.maxTotalEntries
|
|
118717
|
+
this.maxTotalEntries = options.maxTotalEntries === void 0 ? DEFAULT_MAX_TOTAL_LOG_ENTRIES : options.maxTotalEntries;
|
|
118460
118718
|
this.pruneLevel = options.pruneLevel ?? "debug";
|
|
118719
|
+
this.maxBuckets = options.maxBuckets === void 0 ? 128 : options.maxBuckets;
|
|
118461
118720
|
}
|
|
118462
118721
|
/**
|
|
118463
118722
|
* Apply new limits at runtime. Shrinking takes effect as entries are pushed
|
|
@@ -118470,6 +118729,7 @@ var require_dist3 = __commonJS({
|
|
|
118470
118729
|
if (options.perAddonCapacity !== void 0 && options.perAddonCapacity > 0) this.perAddonCapacity = options.perAddonCapacity;
|
|
118471
118730
|
if (options.maxTotalEntries !== void 0) this.maxTotalEntries = options.maxTotalEntries;
|
|
118472
118731
|
if (options.pruneLevel !== void 0) this.pruneLevel = options.pruneLevel;
|
|
118732
|
+
if (options.maxBuckets !== void 0) this.maxBuckets = options.maxBuckets;
|
|
118473
118733
|
}
|
|
118474
118734
|
bufferFor(key) {
|
|
118475
118735
|
let buf = this.buffers.get(key);
|
|
@@ -118480,34 +118740,82 @@ var require_dist3 = __commonJS({
|
|
|
118480
118740
|
return buf;
|
|
118481
118741
|
}
|
|
118482
118742
|
push(entry) {
|
|
118483
|
-
const
|
|
118743
|
+
const key = addonBucketKey(entry.tags?.addonId);
|
|
118744
|
+
const buf = this.bufferFor(key);
|
|
118484
118745
|
const before = buf.size();
|
|
118485
118746
|
buf.push(entry);
|
|
118486
118747
|
this.totalEntries += buf.size() - before;
|
|
118487
|
-
|
|
118748
|
+
this.lastWriteSeq.set(key, ++this.writeSeq);
|
|
118749
|
+
if (this.maxBuckets !== null && this.buffers.size > this.maxBuckets) this.evictBuckets(this.buffers.size - this.maxBuckets, key);
|
|
118750
|
+
if (this.maxTotalEntries !== null && this.totalEntries > this.maxTotalEntries) this.enforceTotalCap(this.maxTotalEntries, key);
|
|
118488
118751
|
}
|
|
118489
118752
|
/**
|
|
118490
|
-
* Bring the total down to `PRUNE_TARGET_RATIO × cap
|
|
118491
|
-
*
|
|
118492
|
-
*
|
|
118493
|
-
*
|
|
118494
|
-
|
|
118495
|
-
|
|
118753
|
+
* Bring the total down to `PRUNE_TARGET_RATIO × cap`.
|
|
118754
|
+
*
|
|
118755
|
+
* Two passes, in this order and no other: first discard prunable entries,
|
|
118756
|
+
* largest bucket first, which is free of information cost. Only if that
|
|
118757
|
+
* cannot reach the target does the second pass drop WHOLE buckets,
|
|
118758
|
+
* least-recently-written first — the bound has to hold even when every
|
|
118759
|
+
* retained entry is above `pruneLevel`, and a bucket nobody has written to is
|
|
118760
|
+
* the cheapest thing in the structure to lose.
|
|
118761
|
+
*/
|
|
118762
|
+
enforceTotalCap(cap, protectedKey) {
|
|
118496
118763
|
const target = Math.max(1, Math.floor(cap * PRUNE_TARGET_RATIO));
|
|
118497
118764
|
let deficit = this.totalEntries - target;
|
|
118498
118765
|
if (deficit <= 0) return;
|
|
118499
|
-
const bySizeDesc = [...this.buffers.
|
|
118500
|
-
for (const buf of bySizeDesc) {
|
|
118766
|
+
const bySizeDesc = [...this.buffers.entries()].toSorted((a, b) => b[1].size() - a[1].size());
|
|
118767
|
+
for (const [key, buf] of bySizeDesc) {
|
|
118501
118768
|
if (deficit <= 0) break;
|
|
118502
118769
|
const removed = buf.pruneOldestAtOrBelow(this.pruneLevel, deficit);
|
|
118503
118770
|
this.totalEntries -= removed;
|
|
118504
118771
|
deficit -= removed;
|
|
118772
|
+
if (buf.size() === 0) this.dropBucket(key);
|
|
118505
118773
|
}
|
|
118774
|
+
if (deficit > 0) this.evictBucketsUntil(target, protectedKey);
|
|
118775
|
+
}
|
|
118776
|
+
/**
|
|
118777
|
+
* Drop whole buckets, least-recently-WRITTEN first, until the total is at or
|
|
118778
|
+
* below `target`. Never touches `protectedKey` (the bucket being written to
|
|
118779
|
+
* right now) and always leaves at least one bucket standing.
|
|
118780
|
+
*/
|
|
118781
|
+
evictBucketsUntil(target, protectedKey) {
|
|
118782
|
+
for (const key of this.evictionOrder(protectedKey)) {
|
|
118783
|
+
if (this.totalEntries <= target || this.buffers.size <= 1) return;
|
|
118784
|
+
this.dropBucket(key);
|
|
118785
|
+
}
|
|
118786
|
+
}
|
|
118787
|
+
/** Drop exactly `count` buckets, least-recently-written first. */
|
|
118788
|
+
evictBuckets(count, protectedKey) {
|
|
118789
|
+
let remaining = count;
|
|
118790
|
+
for (const key of this.evictionOrder(protectedKey)) {
|
|
118791
|
+
if (remaining <= 0 || this.buffers.size <= 1) return;
|
|
118792
|
+
this.dropBucket(key);
|
|
118793
|
+
remaining -= 1;
|
|
118794
|
+
}
|
|
118795
|
+
}
|
|
118796
|
+
/** Bucket keys ordered least-recently-written first, excluding `protectedKey`.
|
|
118797
|
+
* A bucket with no recorded write sorts first — it can only be a leftover. */
|
|
118798
|
+
evictionOrder(protectedKey) {
|
|
118799
|
+
return [...this.buffers.keys()].filter((key) => key !== protectedKey).toSorted((a, b) => (this.lastWriteSeq.get(a) ?? 0) - (this.lastWriteSeq.get(b) ?? 0));
|
|
118800
|
+
}
|
|
118801
|
+
/** Remove a bucket and everything that indexes it. An empty ring left behind
|
|
118802
|
+
* is the leak this class shipped with — the map only ever grew. */
|
|
118803
|
+
dropBucket(key) {
|
|
118804
|
+
const buf = this.buffers.get(key);
|
|
118805
|
+
if (buf === void 0) return;
|
|
118806
|
+
this.totalEntries -= buf.size();
|
|
118807
|
+
this.buffers.delete(key);
|
|
118808
|
+
this.lastWriteSeq.delete(key);
|
|
118506
118809
|
}
|
|
118507
118810
|
/** Entries retained across every bucket. */
|
|
118508
118811
|
size() {
|
|
118509
118812
|
return this.totalEntries;
|
|
118510
118813
|
}
|
|
118814
|
+
/** Buckets currently retained. Observability for the aggregate bound — an
|
|
118815
|
+
* addon roster that only ever grows is what this class had to stop doing. */
|
|
118816
|
+
bucketCount() {
|
|
118817
|
+
return this.buffers.size;
|
|
118818
|
+
}
|
|
118511
118819
|
/** Every retained entry across all buckets, newest-first (mirrors
|
|
118512
118820
|
* {@link LogRingBuffer.getAll}). Used to replay history to a destination. */
|
|
118513
118821
|
getAll() {
|
|
@@ -118535,13 +118843,25 @@ var require_dist3 = __commonJS({
|
|
|
118535
118843
|
if (pinned !== null) {
|
|
118536
118844
|
const removed2 = this.buffers.get(pinned)?.clear(filter) ?? 0;
|
|
118537
118845
|
this.totalEntries -= removed2;
|
|
118846
|
+
this.dropIfEmpty(pinned);
|
|
118538
118847
|
return removed2;
|
|
118539
118848
|
}
|
|
118540
118849
|
let removed = 0;
|
|
118541
|
-
for (const buf of this.buffers.
|
|
118850
|
+
for (const [key, buf] of [...this.buffers.entries()]) {
|
|
118851
|
+
removed += buf.clear(filter);
|
|
118852
|
+
this.dropIfEmpty(key);
|
|
118853
|
+
}
|
|
118542
118854
|
this.totalEntries -= removed;
|
|
118543
118855
|
return removed;
|
|
118544
118856
|
}
|
|
118857
|
+
/** An emptied bucket is removed rather than left as an empty ring: the map
|
|
118858
|
+
* used to grow with every addon id that ever logged and never shrink. */
|
|
118859
|
+
dropIfEmpty(key) {
|
|
118860
|
+
if (this.buffers.get(key)?.size() === 0) {
|
|
118861
|
+
this.buffers.delete(key);
|
|
118862
|
+
this.lastWriteSeq.delete(key);
|
|
118863
|
+
}
|
|
118864
|
+
}
|
|
118545
118865
|
/** The bucket key a filter pins to via `tags.addonId`, or null when the
|
|
118546
118866
|
* filter doesn't constrain the addon (→ scan all buckets). */
|
|
118547
118867
|
pinnedAddonKey(filter) {
|
|
@@ -118637,11 +118957,13 @@ var require_dist3 = __commonJS({
|
|
|
118637
118957
|
* `perAddonCapacity` bounds EACH addon's bucket, not the total — a chatty addon
|
|
118638
118958
|
* evicts only its own lines, so quiet addons keep their sparse history.
|
|
118639
118959
|
*
|
|
118640
|
-
* `options.maxTotalEntries`
|
|
118641
|
-
* per-bucket bound scales with addon count
|
|
118642
|
-
*
|
|
118643
|
-
* `
|
|
118644
|
-
*
|
|
118960
|
+
* `options.maxTotalEntries` ceilings the total across all buckets, because the
|
|
118961
|
+
* per-bucket bound scales with addon count, and `options.maxBuckets` ceilings the
|
|
118962
|
+
* bucket count itself. When the total is exceeded, entries at or below
|
|
118963
|
+
* `options.pruneLevel` (default `debug`) are discarded first — a live addon's
|
|
118964
|
+
* `warn`/`error` are never evicted to satisfy it — and only if that cannot reach
|
|
118965
|
+
* the target are whole least-recently-written buckets dropped. Both bounds are ON
|
|
118966
|
+
* by default; pass an explicit `null` to opt out. See {@link PartitionedLogBuffer}.
|
|
118645
118967
|
*/
|
|
118646
118968
|
constructor(perAddonCapacity = 5e3, options = {}) {
|
|
118647
118969
|
this.ringBuffer = new PartitionedLogBuffer(perAddonCapacity, options);
|
|
@@ -197242,22 +197564,33 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
197242
197564
|
});
|
|
197243
197565
|
});
|
|
197244
197566
|
}
|
|
197567
|
+
var EMPTY_HEAP_DECLARATION = {
|
|
197568
|
+
profile: void 0,
|
|
197569
|
+
maxOldSpaceMb: void 0
|
|
197570
|
+
};
|
|
197245
197571
|
var heapProfileCache = /* @__PURE__ */ new Map();
|
|
197246
|
-
function
|
|
197572
|
+
function readAddonHeapDeclaration(spec) {
|
|
197247
197573
|
const cacheKey = `${spec.addonDir}::${spec.addonId}`;
|
|
197248
197574
|
const cached = heapProfileCache.get(cacheKey);
|
|
197249
|
-
if (cached !== void 0
|
|
197250
|
-
let
|
|
197575
|
+
if (cached !== void 0) return cached;
|
|
197576
|
+
let declaration = EMPTY_HEAP_DECLARATION;
|
|
197251
197577
|
for (const manifestPath of [node_path.join(spec.addonDir, "package.json"), node_path.join(node_path.dirname(spec.addonDir), "package.json")]) try {
|
|
197252
197578
|
const raw = node_fs.readFileSync(manifestPath, "utf8");
|
|
197253
197579
|
const parsed = JSON.parse(raw);
|
|
197254
|
-
profile = extractHeapProfile(parsed, spec.addonId);
|
|
197255
|
-
|
|
197580
|
+
const profile = extractHeapProfile(parsed, spec.addonId);
|
|
197581
|
+
const maxOldSpaceMb = extractMaxOldSpaceMb(parsed, spec.addonId);
|
|
197582
|
+
if (profile !== void 0 || maxOldSpaceMb !== void 0) {
|
|
197583
|
+
declaration = {
|
|
197584
|
+
profile,
|
|
197585
|
+
maxOldSpaceMb
|
|
197586
|
+
};
|
|
197587
|
+
break;
|
|
197588
|
+
}
|
|
197256
197589
|
if (manifestHasAddon(parsed, spec.addonId)) break;
|
|
197257
197590
|
} catch {
|
|
197258
197591
|
}
|
|
197259
|
-
heapProfileCache.set(cacheKey,
|
|
197260
|
-
return
|
|
197592
|
+
heapProfileCache.set(cacheKey, declaration);
|
|
197593
|
+
return declaration;
|
|
197261
197594
|
}
|
|
197262
197595
|
function invalidateHeapProfiles(addons) {
|
|
197263
197596
|
for (const spec of addons) heapProfileCache.delete(`${spec.addonDir}::${spec.addonId}`);
|
|
@@ -197269,6 +197602,10 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
197269
197602
|
const value = readManifestAddons(parsed).find((a) => a.id === addonId)?.execution?.heapProfile;
|
|
197270
197603
|
return value === "heavy" || value === "light" ? value : void 0;
|
|
197271
197604
|
}
|
|
197605
|
+
function extractMaxOldSpaceMb(parsed, addonId) {
|
|
197606
|
+
const value = readManifestAddons(parsed).find((a) => a.id === addonId)?.execution?.maxOldSpaceMb;
|
|
197607
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : void 0;
|
|
197608
|
+
}
|
|
197272
197609
|
function readManifestAddons(parsed) {
|
|
197273
197610
|
if (typeof parsed !== "object" || parsed === null) return [];
|
|
197274
197611
|
const camstack = parsed.camstack;
|
|
@@ -197277,15 +197614,25 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
197277
197614
|
return Array.isArray(addons) ? addons : [];
|
|
197278
197615
|
}
|
|
197279
197616
|
function isHeavyRunner(addons) {
|
|
197280
|
-
return addons.some((a) =>
|
|
197617
|
+
return addons.some((a) => readAddonHeapDeclaration(a).profile === "heavy");
|
|
197618
|
+
}
|
|
197619
|
+
var HEAVY_MAX_OLD_MB_DEFAULT = 1024;
|
|
197620
|
+
function runnerMaxOldSpaceMb(addons, heavy) {
|
|
197621
|
+
const envDefault = heavy ? positiveIntEnv("CAMSTACK_RUNNER_HEAVY_MAX_OLD_MB", HEAVY_MAX_OLD_MB_DEFAULT) : positiveIntEnv("CAMSTACK_RUNNER_LIGHT_MAX_OLD_MB", 0);
|
|
197622
|
+
const declared = addons.map((a) => readAddonHeapDeclaration(a).maxOldSpaceMb).filter((mb) => mb !== void 0);
|
|
197623
|
+
if (declared.length === 0) return envDefault;
|
|
197624
|
+
if (declared.includes(0)) return 0;
|
|
197625
|
+
return Math.max(...declared);
|
|
197281
197626
|
}
|
|
197282
197627
|
function runnerHeapFlags(addons) {
|
|
197283
197628
|
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);
|
|
197629
|
+
const heavy = isHeavyRunner(addons);
|
|
197287
197630
|
const flags = [];
|
|
197288
|
-
if (
|
|
197631
|
+
if (!heavy) {
|
|
197632
|
+
const semiMb = positiveIntEnv("CAMSTACK_RUNNER_LIGHT_SEMI_SPACE_MB", 2);
|
|
197633
|
+
if (semiMb > 0) flags.push(`--max-semi-space-size=${semiMb}`);
|
|
197634
|
+
}
|
|
197635
|
+
const oldMb = runnerMaxOldSpaceMb(addons, heavy);
|
|
197289
197636
|
if (oldMb > 0) flags.push(`--max-old-space-size=${oldMb}`);
|
|
197290
197637
|
return flags;
|
|
197291
197638
|
}
|
|
@@ -197332,19 +197679,21 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
197332
197679
|
function spawnRunner(runnerId, addons, env) {
|
|
197333
197680
|
const nodeId = buildNodeId(runnerId);
|
|
197334
197681
|
const runnerPath = node_path.resolve(__dirname, "addon-runner.js");
|
|
197682
|
+
const heavy = isHeavyRunner(addons);
|
|
197335
197683
|
const childEnv = {
|
|
197336
197684
|
...process.env,
|
|
197337
197685
|
CAMSTACK_RUNNER_ID: runnerId,
|
|
197338
197686
|
CAMSTACK_RUNNER_ADDONS: JSON.stringify(addons),
|
|
197339
197687
|
CAMSTACK_PARENT_NODE_ID: parentNodeId,
|
|
197340
197688
|
CAMSTACK_DATA_DIR: dataDir,
|
|
197689
|
+
CAMSTACK_RUNNER_HEAP_PROFILE: heavy ? "heavy" : "light",
|
|
197341
197690
|
CAMSTACK_LOG_LEVEL: process.env["CAMSTACK_LOG_LEVEL"] ?? "info",
|
|
197342
197691
|
...parentTcpPort !== void 0 ? { CAMSTACK_PARENT_TCP_PORT: String(parentTcpPort) } : {},
|
|
197343
197692
|
...parentUdsPath !== void 0 ? { CAMSTACK_PARENT_UDS_PATH: parentUdsPath } : {},
|
|
197344
197693
|
...env
|
|
197345
197694
|
};
|
|
197346
197695
|
const heapFlags = runnerHeapFlags(addons);
|
|
197347
|
-
capturedBroker?.logger.info(`[${runnerId}] heap profile: ${
|
|
197696
|
+
capturedBroker?.logger.info(`[${runnerId}] heap profile: ${heavy ? "heavy" : "light"} flags=[${heapFlags.join(" ")}]`);
|
|
197348
197697
|
const child = spawnFn(process.execPath, [...heapFlags, runnerPath], {
|
|
197349
197698
|
env: childEnv,
|
|
197350
197699
|
stdio: [
|
|
@@ -197388,6 +197737,15 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
197388
197737
|
capturedBroker?.logger.error(`[${runnerId}] Crash circuit-breaker tripped \u2014 ${decision.crashesInWindow} crashes in ${CRASH_WINDOW_MS}ms. Not respawning.`);
|
|
197389
197738
|
processes.set(runnerId, entry);
|
|
197390
197739
|
if (capturedBroker) forceMoleculerDisconnect(capturedBroker, entry.nodeId);
|
|
197740
|
+
emitRunnerEvent(require_dist10.EventCategory.AddonRunnerFailed, runnerId, {
|
|
197741
|
+
runnerId,
|
|
197742
|
+
nodeId: parentNodeId,
|
|
197743
|
+
addonIds: entry.runnerAddons.map((addon) => addon.addonId),
|
|
197744
|
+
crashesInWindow: decision.crashesInWindow,
|
|
197745
|
+
windowMs: CRASH_WINDOW_MS,
|
|
197746
|
+
...code !== null ? { exitCode: code } : {},
|
|
197747
|
+
...signal !== null ? { signal } : {}
|
|
197748
|
+
});
|
|
197391
197749
|
for (const addon of entry.runnerAddons) emitRunnerEvent(require_dist10.EventCategory.AddonCrashed, addon.addonId, {
|
|
197392
197750
|
addonId: addon.addonId,
|
|
197393
197751
|
error: `Crash circuit-breaker tripped after ${decision.crashesInWindow} crashes in ${CRASH_WINDOW_MS}ms`
|
|
@@ -198109,10 +198467,10 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198109
198467
|
exports.HEALTH_MONITOR_GRACE_PERIOD_MS = HEALTH_MONITOR_GRACE_PERIOD_MS;
|
|
198110
198468
|
exports.HEALTH_MONITOR_RETRY_INTERVALS_MS = HEALTH_MONITOR_RETRY_INTERVALS_MS;
|
|
198111
198469
|
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;
|
|
198470
|
+
exports.HEAP_RECLAIM_MIN_INTERVAL_MS = require_manifest_python_deps.HEAP_RECLAIM_MIN_INTERVAL_MS;
|
|
198471
|
+
exports.HEAP_RECLAIM_TRIGGER_MB = require_manifest_python_deps.HEAP_RECLAIM_TRIGGER_MB;
|
|
198472
|
+
exports.HEAP_WATCH_INTERVAL_MS = require_manifest_python_deps.HEAP_WATCH_INTERVAL_MS;
|
|
198473
|
+
exports.HEAP_WATCH_WARN_RATIO = require_manifest_python_deps.HEAP_WATCH_WARN_RATIO;
|
|
198116
198474
|
exports.HUB_CAP_FWD_ACTION = require_manifest_python_deps.HUB_CAP_FWD_ACTION;
|
|
198117
198475
|
exports.HUB_CAP_FWD_SERVICE = require_manifest_python_deps.HUB_CAP_FWD_SERVICE;
|
|
198118
198476
|
exports.HubForwarderAddon = require_builtins_hub_forwarder_index.HubForwarderAddon$1;
|
|
@@ -198151,6 +198509,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198151
198509
|
exports.PythonEnvManager = PythonEnvManager;
|
|
198152
198510
|
exports.QUARANTINE_DIRNAME = QUARANTINE_DIRNAME;
|
|
198153
198511
|
exports.RESTART_MARKER_FILE = RESTART_MARKER_FILE;
|
|
198512
|
+
exports.RUNNER_HEAP_WATCH_INTERVAL_MS = require_manifest_python_deps.RUNNER_HEAP_WATCH_INTERVAL_MS;
|
|
198154
198513
|
exports.RUNTIME_DEFAULTS = require_dist10.RUNTIME_DEFAULTS;
|
|
198155
198514
|
exports.ReadinessRegistry = require_dist10.ReadinessRegistry;
|
|
198156
198515
|
exports.ReadinessTimeoutError = require_dist10.ReadinessTimeoutError;
|
|
@@ -198189,7 +198548,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198189
198548
|
}
|
|
198190
198549
|
});
|
|
198191
198550
|
exports.buildCapRouters = buildCapRouters;
|
|
198192
|
-
exports.buildHeapSample = buildHeapSample;
|
|
198551
|
+
exports.buildHeapSample = require_manifest_python_deps.buildHeapSample;
|
|
198193
198552
|
exports.buildLinkChain = require_manifest_python_deps.buildLinkChain;
|
|
198194
198553
|
exports.buildNativeCapProxy = require_manifest_python_deps.buildNativeCapProxy;
|
|
198195
198554
|
exports.buildNodeManifest = buildNodeManifest;
|
|
@@ -198234,7 +198593,7 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198234
198593
|
exports.createUdsEventBus = require_manifest_python_deps.createUdsEventBus;
|
|
198235
198594
|
exports.createUdsLogger = require_manifest_python_deps.createUdsLogger;
|
|
198236
198595
|
exports.createUdsLoggerWithControl = require_manifest_python_deps.createUdsLoggerWithControl;
|
|
198237
|
-
exports.createV8Reclaimer = createV8Reclaimer;
|
|
198596
|
+
exports.createV8Reclaimer = require_manifest_python_deps.createV8Reclaimer;
|
|
198238
198597
|
exports.deleteModelFromDisk = require_file_data_plane.deleteModelFromDisk;
|
|
198239
198598
|
exports.deriveAgentListenPort = deriveAgentListenPort;
|
|
198240
198599
|
exports.describeProviderKindDrift = describeProviderKindDrift;
|
|
@@ -198360,9 +198719,10 @@ globstar while`, t, d, e, f, m), this.matchOne(t.slice(d), e.slice(f), s)) retur
|
|
|
198360
198719
|
exports.serializeTypedArrays = require_manifest_python_deps.serializeTypedArrays;
|
|
198361
198720
|
exports.setHubConnected = require_manifest_python_deps.setHubConnected;
|
|
198362
198721
|
exports.setNodeEventInterest = require_manifest_python_deps.setNodeEventInterest;
|
|
198363
|
-
exports.shouldReclaim = shouldReclaim;
|
|
198364
|
-
exports.startHeapWatch = startHeapWatch;
|
|
198365
|
-
exports.
|
|
198722
|
+
exports.shouldReclaim = require_manifest_python_deps.shouldReclaim;
|
|
198723
|
+
exports.startHeapWatch = require_manifest_python_deps.startHeapWatch;
|
|
198724
|
+
exports.startRunnerHeapWatch = require_manifest_python_deps.startRunnerHeapWatch;
|
|
198725
|
+
exports.strandedMb = require_manifest_python_deps.strandedMb;
|
|
198366
198726
|
exports.stripCamstackDeps = stripCamstackDeps;
|
|
198367
198727
|
exports.subscribePassthrough = require_manifest_python_deps.subscribePassthrough;
|
|
198368
198728
|
exports.udsChildLogToWorkerEntry = require_manifest_python_deps.udsChildLogToWorkerEntry;
|
|
@@ -237035,7 +237395,7 @@ var require_enums = __commonJS({
|
|
|
237035
237395
|
"../types/dist/enums.js"(exports) {
|
|
237036
237396
|
"use strict";
|
|
237037
237397
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
237038
|
-
var require_event_category =
|
|
237398
|
+
var require_event_category = require_event_category_CRPORAAz();
|
|
237039
237399
|
var EventSourceType = /* @__PURE__ */ (function(EventSourceType2) {
|
|
237040
237400
|
EventSourceType2["Addon"] = "addon";
|
|
237041
237401
|
EventSourceType2["Core"] = "core";
|
|
@@ -237055,8 +237415,8 @@ var require_dist9 = __commonJS({
|
|
|
237055
237415
|
"../types/dist/index.js"(exports) {
|
|
237056
237416
|
"use strict";
|
|
237057
237417
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
237058
|
-
var require_event_category =
|
|
237059
|
-
var require_sleep =
|
|
237418
|
+
var require_event_category = require_event_category_CRPORAAz();
|
|
237419
|
+
var require_sleep = require_sleep_CMRLJj2e();
|
|
237060
237420
|
var require_canonical_hash = require_canonical_hash_DNV8S5ET();
|
|
237061
237421
|
var require_enums2 = require_enums();
|
|
237062
237422
|
var require_err_msg = require_err_msg_COpsHMw2();
|
|
@@ -237978,6 +238338,7 @@ var require_dist9 = __commonJS({
|
|
|
237978
238338
|
preBufferSec: 10,
|
|
237979
238339
|
postBufferSec: 30
|
|
237980
238340
|
};
|
|
238341
|
+
var VISIT_MERGE_GAP_MS = DEFAULT_EVENTS_BAND_BUFFER_SEC.postBufferSec * 1e3;
|
|
237981
238342
|
var RecordingRetentionSchema = zod.z.object({
|
|
237982
238343
|
maxAgeDays: zod.z.number().min(0).optional(),
|
|
237983
238344
|
maxSizeGb: zod.z.number().min(0).optional()
|
|
@@ -238039,6 +238400,12 @@ var require_dist9 = __commonJS({
|
|
|
238039
238400
|
/** DERIVED summary of `bands`, stamped by the recorder on every save.
|
|
238040
238401
|
* Authoring it has no effect — see {@link RecordingStorageModeSchema}. */
|
|
238041
238402
|
mode: RecordingStorageModeSchema.optional(),
|
|
238403
|
+
/**
|
|
238404
|
+
* Which assigned broker slots to record. Absent / empty = {@link
|
|
238405
|
+
* DEFAULT_RECORDING_PROFILES} (`high`+`low`) intersected with the
|
|
238406
|
+
* camera's currently assigned slots — never `mid` unless the operator
|
|
238407
|
+
* picks it, and never a slot the broker has not assigned.
|
|
238408
|
+
*/
|
|
238042
238409
|
profiles: zod.z.array(require_sleep.CamProfileSchema).optional(),
|
|
238043
238410
|
segmentSeconds: zod.z.number().int().positive().optional(),
|
|
238044
238411
|
/**
|
|
@@ -238061,6 +238428,15 @@ var require_dist9 = __commonJS({
|
|
|
238061
238428
|
if (config.bands.some((band) => band.mode === "continuous")) return "continuous";
|
|
238062
238429
|
return "events";
|
|
238063
238430
|
}
|
|
238431
|
+
var DEFAULT_RECORDING_PROFILES = ["high", "low"];
|
|
238432
|
+
function resolveRecordingProfiles(assigned, override) {
|
|
238433
|
+
if (override !== void 0 && override.length > 0) {
|
|
238434
|
+
const selected = assigned.filter((profile) => override.includes(profile));
|
|
238435
|
+
return selected.length > 0 ? selected : [...assigned];
|
|
238436
|
+
}
|
|
238437
|
+
const preferred = assigned.filter((profile) => DEFAULT_RECORDING_PROFILES.includes(profile));
|
|
238438
|
+
return preferred.length > 0 ? preferred : [...assigned];
|
|
238439
|
+
}
|
|
238064
238440
|
var RelocateJobStateSchema = zod.z.enum([
|
|
238065
238441
|
"queued",
|
|
238066
238442
|
"running",
|
|
@@ -238106,7 +238482,11 @@ var require_dist9 = __commonJS({
|
|
|
238106
238482
|
profiles: zod.z.array(zod.z.string()).optional(),
|
|
238107
238483
|
/** Copy throttle in MB/s (default 40) — the drain is a background chore,
|
|
238108
238484
|
* never allowed to starve live writers. */
|
|
238109
|
-
throttleMbps: zod.z.number().min(1).max(1e3).optional()
|
|
238485
|
+
throttleMbps: zod.z.number().min(1).max(1e3).optional(),
|
|
238486
|
+
/** Move only segments whose startMs is >= this. Absent = the whole source
|
|
238487
|
+
* pile. Used when a full drain is too expensive and the operator only
|
|
238488
|
+
* wants the recent window on the new disk. */
|
|
238489
|
+
sinceMs: zod.z.number().int().optional()
|
|
238110
238490
|
});
|
|
238111
238491
|
var StorageMigrationLeaseInputSchema = zod.z.object({ leaseId: zod.z.string().min(1) });
|
|
238112
238492
|
var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: zod.z.string().min(1) });
|
|
@@ -246609,6 +246989,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
246609
246989
|
"node-offline",
|
|
246610
246990
|
"node-inference-unavailable",
|
|
246611
246991
|
"detection-blind",
|
|
246992
|
+
"addon-crash-loop",
|
|
246612
246993
|
"addon-update-available",
|
|
246613
246994
|
"server-update-available",
|
|
246614
246995
|
"alarm-triggered",
|
|
@@ -246616,6 +246997,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
246616
246997
|
"alarm-disarmed",
|
|
246617
246998
|
"alarm-arming",
|
|
246618
246999
|
"alarm-arm-refused",
|
|
247000
|
+
"addon-updated",
|
|
247001
|
+
"server-updated",
|
|
247002
|
+
"export-completed",
|
|
246619
247003
|
"camera-online",
|
|
246620
247004
|
"camera-offline",
|
|
246621
247005
|
"camera-disabled",
|
|
@@ -247445,6 +247829,10 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
247445
247829
|
value: "detection-blind",
|
|
247446
247830
|
label: "Camera detecting nothing"
|
|
247447
247831
|
},
|
|
247832
|
+
{
|
|
247833
|
+
value: "addon-crash-loop",
|
|
247834
|
+
label: "Addon stopped after repeated crashes"
|
|
247835
|
+
},
|
|
247448
247836
|
{
|
|
247449
247837
|
value: "addon-update-available",
|
|
247450
247838
|
label: "Addon update available"
|
|
@@ -247453,6 +247841,18 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
247453
247841
|
value: "server-update-available",
|
|
247454
247842
|
label: "Server update available"
|
|
247455
247843
|
},
|
|
247844
|
+
{
|
|
247845
|
+
value: "addon-updated",
|
|
247846
|
+
label: "Addons updated"
|
|
247847
|
+
},
|
|
247848
|
+
{
|
|
247849
|
+
value: "server-updated",
|
|
247850
|
+
label: "Server updated"
|
|
247851
|
+
},
|
|
247852
|
+
{
|
|
247853
|
+
value: "export-completed",
|
|
247854
|
+
label: "Export completed"
|
|
247855
|
+
},
|
|
247456
247856
|
{
|
|
247457
247857
|
value: "alarm-arming",
|
|
247458
247858
|
label: "Alarm arming (exit delay)"
|
|
@@ -248469,7 +248869,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
248469
248869
|
* `'staging'`. */
|
|
248470
248870
|
markForTrain: zod.z.boolean().optional(),
|
|
248471
248871
|
/** Operator marked this track for diagnostic attention. */
|
|
248472
|
-
debug: zod.z.boolean().optional()
|
|
248872
|
+
debug: zod.z.boolean().optional(),
|
|
248873
|
+
/** Operator favourited this track. Pins it against pruning. */
|
|
248874
|
+
favourited: zod.z.boolean().optional()
|
|
248473
248875
|
};
|
|
248474
248876
|
var TrackRetrainFields = { retrainStatus: RetrainStatusSchema.optional() };
|
|
248475
248877
|
var TrackFlagsPatchSchema = zod.z.object(TrackFlagFields);
|
|
@@ -248477,6 +248879,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
248477
248879
|
trackId: zod.z.string(),
|
|
248478
248880
|
markForTrain: zod.z.boolean(),
|
|
248479
248881
|
debug: zod.z.boolean(),
|
|
248882
|
+
favourited: zod.z.boolean(),
|
|
248480
248883
|
/** The lifecycle state the boolean was derived from. Required here (unlike on
|
|
248481
248884
|
* a track row) because this shape is only ever produced by the write body,
|
|
248482
248885
|
* which always knows it — and a surface that has just written needs to render
|
|
@@ -248999,6 +249402,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
248999
249402
|
/** Per-track CLIP search vectors removed (best-effort). */
|
|
249000
249403
|
embeddings: zod.z.number().int()
|
|
249001
249404
|
});
|
|
249405
|
+
var DiskReconcileCountsSchema = zod.z.object({
|
|
249406
|
+
mediaDropped: zod.z.number().int(),
|
|
249407
|
+
tracks: zod.z.number().int(),
|
|
249408
|
+
events: zod.z.number().int()
|
|
249409
|
+
});
|
|
249002
249410
|
var EventStoreDeviceFootprintSchema = zod.z.object({
|
|
249003
249411
|
deviceId: zod.z.number(),
|
|
249004
249412
|
/** Persisted event rows (motion + object + audio) for the camera. */
|
|
@@ -249255,6 +249663,17 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
249255
249663
|
auth: "admin"
|
|
249256
249664
|
}),
|
|
249257
249665
|
/**
|
|
249666
|
+
* Disk-wins reconcile for one camera. Drops media index rows whose blobs
|
|
249667
|
+
* are gone, then cascades tracks (including favourited and staging) that
|
|
249668
|
+
* have no remaining files. Enrolled identity/vehicle/scene media is never
|
|
249669
|
+
* probed. Trackless motion/audio events with no remaining file are dropped,
|
|
249670
|
+
* including snapshot-less rows.
|
|
249671
|
+
*/
|
|
249672
|
+
reconcileFromDisk: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), DiskReconcileCountsSchema, {
|
|
249673
|
+
kind: "mutation",
|
|
249674
|
+
auth: "admin"
|
|
249675
|
+
}),
|
|
249676
|
+
/**
|
|
249258
249677
|
* Delete whole tracks (object events) by id for the given device,
|
|
249259
249678
|
* cascading their media in lockstep. Returns the number of tracks
|
|
249260
249679
|
* actually deleted plus the ids that could not be removed.
|
|
@@ -249273,7 +249692,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
249273
249692
|
auth: "admin"
|
|
249274
249693
|
}),
|
|
249275
249694
|
/**
|
|
249276
|
-
* Set the per-track operator flags (`markForTrain`, `debug`) on ONE track.
|
|
249695
|
+
* Set the per-track operator flags (`markForTrain`, `debug`, `favourited`) on ONE track.
|
|
249277
249696
|
* The patch is PARTIAL — an omitted key is left untouched — because the
|
|
249278
249697
|
* three surfaces that write it (admin Events grid, viewer track detail,
|
|
249279
249698
|
* viewer cluster detail) each own one toggle and must not clobber the other.
|
|
@@ -251261,6 +251680,24 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
251261
251680
|
/** Wall-clock ms spent on the stage before it was abandoned. */
|
|
251262
251681
|
elapsedMs: zod.z.number()
|
|
251263
251682
|
});
|
|
251683
|
+
var DiskReconcileJobSchema = zod.z.object({
|
|
251684
|
+
state: zod.z.enum([
|
|
251685
|
+
"idle",
|
|
251686
|
+
"running",
|
|
251687
|
+
"done",
|
|
251688
|
+
"error"
|
|
251689
|
+
]),
|
|
251690
|
+
total: zod.z.number().int().nonnegative(),
|
|
251691
|
+
completed: zod.z.number().int().nonnegative(),
|
|
251692
|
+
currentDeviceId: zod.z.number().int().nullable(),
|
|
251693
|
+
failed: zod.z.array(zod.z.number().int()).readonly(),
|
|
251694
|
+
mediaDropped: zod.z.number().int().nonnegative(),
|
|
251695
|
+
tracks: zod.z.number().int().nonnegative(),
|
|
251696
|
+
events: zod.z.number().int().nonnegative(),
|
|
251697
|
+
startedAtMs: zod.z.number().int().nullable(),
|
|
251698
|
+
finishedAtMs: zod.z.number().int().nullable(),
|
|
251699
|
+
error: zod.z.string().nullable()
|
|
251700
|
+
});
|
|
251264
251701
|
var CameraStatusSchema = zod.z.object({
|
|
251265
251702
|
deviceId: zod.z.number(),
|
|
251266
251703
|
assignment: CameraAssignmentStatusSchema,
|
|
@@ -251785,6 +252222,18 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
251785
252222
|
* rail without issuing N parallel browser round-trips.
|
|
251786
252223
|
*/
|
|
251787
252224
|
getCameraStatuses: require_sleep.method(zod.z.object({ deviceIds: zod.z.array(zod.z.number()).optional() }), zod.z.array(CameraStatusSchema).readonly()),
|
|
252225
|
+
/**
|
|
252226
|
+
* Disk-wins fleet reconcile after a recordings wipe. Starts the walk in
|
|
252227
|
+
* the addon process and returns immediately with the job snapshot — the
|
|
252228
|
+
* work outlives the tRPC/UDS 60s timeout. Poll `getReconcileFromDiskStatus`.
|
|
252229
|
+
* Admin-only. Idempotent while a job is already running.
|
|
252230
|
+
*/
|
|
252231
|
+
reconcileFromDisk: require_sleep.method(zod.z.void(), DiskReconcileJobSchema, {
|
|
252232
|
+
kind: "mutation",
|
|
252233
|
+
auth: "admin"
|
|
252234
|
+
}),
|
|
252235
|
+
/** Snapshot of the in-flight or last disk-wins fleet reconcile. */
|
|
252236
|
+
getReconcileFromDiskStatus: require_sleep.method(zod.z.void(), DiskReconcileJobSchema, { auth: "admin" }),
|
|
251788
252237
|
/** List every template the operator has saved. */
|
|
251789
252238
|
listTemplates: require_sleep.method(zod.z.void(), zod.z.array(PipelineTemplateSchema).readonly()),
|
|
251790
252239
|
/** Create a new named preset from a given CameraPipelineConfig. */
|
|
@@ -252927,6 +253376,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
252927
253376
|
stats: require_sleep.method(VectorStatsInputSchema, VectorStatsResultSchema)
|
|
252928
253377
|
}
|
|
252929
253378
|
};
|
|
253379
|
+
var MAX_CLIP_EVENT_IDS = 24;
|
|
253380
|
+
var MAX_CLIP_LABELS = 3;
|
|
252930
253381
|
var ClipSchema = zod.z.object({
|
|
252931
253382
|
/** Opaque, provider-namespaced id. The default provider encodes the time
|
|
252932
253383
|
* window so `getClipPlayback` is self-contained (no event re-query). */
|
|
@@ -252943,8 +253394,65 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
252943
253394
|
startMs: zod.z.number(),
|
|
252944
253395
|
endMs: zod.z.number()
|
|
252945
253396
|
}),
|
|
252946
|
-
/**
|
|
252947
|
-
|
|
253397
|
+
/**
|
|
253398
|
+
* Distinct object classes attached to this visit (`person`, `car`, …),
|
|
253399
|
+
* dominant first, capped at {@link MAX_CLIP_LABELS}. The ribbon renders these
|
|
253400
|
+
* instead of the bare kind — "Object" tells the operator nothing a colour bar
|
|
253401
|
+
* did not. Absent (never empty) when the visit attached no classified object
|
|
253402
|
+
* event, so a motion/audio-only visit keeps its kind label.
|
|
253403
|
+
*/
|
|
253404
|
+
labels: zod.z.array(zod.z.string()).max(3).optional(),
|
|
253405
|
+
/**
|
|
253406
|
+
* Lazy thumbnail URL, never inlined.
|
|
253407
|
+
*
|
|
253408
|
+
* Recording-derived clips (events-mode keep-window, and the prepared
|
|
253409
|
+
* continuous event+fragment visit) MUST use the snapshot of the **main
|
|
253410
|
+
* event of the interval** — `getEventMedia({ eventId, kind: 'snapshot' })`
|
|
253411
|
+
* of the event that owns `kind` (object > motion > audio). Do not extract
|
|
253412
|
+
* a keyframe from the recorded segments. Other providers (onboard, HKSV)
|
|
253413
|
+
* mint their own stills.
|
|
253414
|
+
*
|
|
253415
|
+
* **VOUCHED, never fabricated.** A provider emits this only for an event it
|
|
253416
|
+
* has CONFIRMED owns at least one media row (its own, or its owning track's).
|
|
253417
|
+
* Absent is meaningful — "this visit has no event still" — never "we did not
|
|
253418
|
+
* look". Stamping it from a URL template made 35% of one camera's clips point
|
|
253419
|
+
* at a 404 (device 3836, 2026-08-17: 64 of 179 clips dead, 63 of them motion).
|
|
253420
|
+
* A read that FAILS drops the claim; it never invents it.
|
|
253421
|
+
*/
|
|
253422
|
+
thumbnail: zod.z.string().optional(),
|
|
253423
|
+
/**
|
|
253424
|
+
* An instant INSIDE this visit's footage, hole-safe, where a recorded still
|
|
253425
|
+
* can be decoded. Present whenever the visit came from recorded availability;
|
|
253426
|
+
* absent on a per-event padded window (there is no footage to promise).
|
|
253427
|
+
*
|
|
253428
|
+
* This is not a thumbnail and not a second byte path: it is the argument to
|
|
253429
|
+
* the recorder's existing still route. The surface — never the provider —
|
|
253430
|
+
* decides whether to use it. It is the midpoint of the visit's LONGEST
|
|
253431
|
+
* contiguous range, not of the visit: a visit spans its holes by
|
|
253432
|
+
* construction, so a naive midpoint lands in dead air.
|
|
253433
|
+
*/
|
|
253434
|
+
stillAtMs: zod.z.number().optional(),
|
|
253435
|
+
/**
|
|
253436
|
+
* Analytics event ids that overlap this visit — a BOUNDED sample, newest
|
|
253437
|
+
* first within kind (object → motion → audio), capped at
|
|
253438
|
+
* {@link MAX_CLIP_EVENT_IDS}. Empty on footage-only clips.
|
|
253439
|
+
*
|
|
253440
|
+
* Bounded because it is not a payload the surface pages through: one visit on
|
|
253441
|
+
* device 615 carried 2 345 ids, and `eventIds` was 99% of a 153 KB
|
|
253442
|
+
* camera-day. Read {@link eventCount} for the true total.
|
|
253443
|
+
*/
|
|
253444
|
+
eventIds: zod.z.array(zod.z.string()).optional(),
|
|
253445
|
+
/** How many analytics events actually overlap this visit. Differs from
|
|
253446
|
+
* `eventIds.length` exactly when the sample was capped — so a truncated
|
|
253447
|
+
* list is never mistaken for a quiet visit. */
|
|
253448
|
+
eventCount: zod.z.number().int().nonnegative().optional(),
|
|
253449
|
+
/** Intra-visit footage holes (GOP rolls, discarded segments) still shorter
|
|
253450
|
+
* than `VISIT_MERGE_GAP_MS`. Playback concatenates around them; the timeline
|
|
253451
|
+
* bar keeps showing them via `recording.getAvailability`. */
|
|
253452
|
+
holes: zod.z.array(zod.z.object({
|
|
253453
|
+
startMs: zod.z.number(),
|
|
253454
|
+
endMs: zod.z.number()
|
|
253455
|
+
})).optional()
|
|
252948
253456
|
});
|
|
252949
253457
|
var ClipPlaybackSchema = zod.z.object({
|
|
252950
253458
|
/** HLS master URL through the hub data-plane (Range + token in path). */
|
|
@@ -253492,7 +254000,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
253492
254000
|
});
|
|
253493
254001
|
var AutoUpdateSettingsSchema = zod.z.object({
|
|
253494
254002
|
channel: ChannelSchema,
|
|
253495
|
-
intervalSeconds: zod.z.number()
|
|
254003
|
+
intervalSeconds: zod.z.number(),
|
|
254004
|
+
/**
|
|
254005
|
+
* Cadence of the "an update exists" POLLER, in seconds. Independent of
|
|
254006
|
+
* `channel`: the poller runs while auto-apply is `off`, because being told
|
|
254007
|
+
* about a publish and installing it are different decisions. Clamped
|
|
254008
|
+
* server-side to 900 s … 604800 s; defaults to 21600 s (6 h).
|
|
254009
|
+
*/
|
|
254010
|
+
updateCheckIntervalSeconds: zod.z.number()
|
|
253496
254011
|
});
|
|
253497
254012
|
var AddonAutoUpdateSchema = ChannelWithInheritSchema;
|
|
253498
254013
|
var RestartAddonResultSchema = zod.z.unknown();
|
|
@@ -253743,7 +254258,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
253743
254258
|
getAutoUpdateSettings: require_sleep.method(zod.z.void(), AutoUpdateSettingsSchema, { auth: "admin" }),
|
|
253744
254259
|
setAutoUpdateSettings: require_sleep.method(zod.z.object({
|
|
253745
254260
|
channel: ChannelSchema,
|
|
253746
|
-
intervalSeconds: zod.z.number().min(300).max(86400).optional()
|
|
254261
|
+
intervalSeconds: zod.z.number().min(300).max(86400).optional(),
|
|
254262
|
+
/** Availability-poll cadence; see AutoUpdateSettingsSchema. */
|
|
254263
|
+
updateCheckIntervalSeconds: zod.z.number().min(900).max(604800).optional()
|
|
253747
254264
|
}), zod.z.unknown(), {
|
|
253748
254265
|
kind: "mutation",
|
|
253749
254266
|
auth: "admin"
|
|
@@ -259033,7 +259550,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
259033
259550
|
includeAudio: zod.z.boolean(),
|
|
259034
259551
|
maxLifeMs: zod.z.number().int().positive(),
|
|
259035
259552
|
deleteAfterDownload: zod.z.boolean(),
|
|
259036
|
-
title: zod.z.string().max(200).optional()
|
|
259553
|
+
title: zod.z.string().max(200).optional(),
|
|
259554
|
+
/** Notification-output target ids to ping when this export becomes ready. */
|
|
259555
|
+
notifyTargetIds: zod.z.array(zod.z.string().min(1)).max(20).optional()
|
|
259037
259556
|
}).superRefine((v, ctx) => {
|
|
259038
259557
|
if (v.speed !== void 0 && v.timelapse !== void 0) ctx.addIssue({
|
|
259039
259558
|
code: zod.z.ZodIssueCode.custom,
|
|
@@ -259083,20 +259602,30 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
259083
259602
|
name: zod.z.string(),
|
|
259084
259603
|
bytes: zod.z.number().int().nonnegative()
|
|
259085
259604
|
});
|
|
259605
|
+
var CreateExportInputSchema = zod.z.object({
|
|
259606
|
+
deviceId: zod.z.number(),
|
|
259607
|
+
/** @deprecated Prefer `profiles`. Kept so timelapse/notifiers keep working. */
|
|
259608
|
+
profile: zod.z.string().optional(),
|
|
259609
|
+
profiles: zod.z.array(zod.z.string()).min(1).optional(),
|
|
259610
|
+
fromMs: zod.z.number(),
|
|
259611
|
+
toMs: zod.z.number(),
|
|
259612
|
+
options: ExportOptionsSchema
|
|
259613
|
+
}).superRefine((v, ctx) => {
|
|
259614
|
+
if ((v.profiles !== void 0 && v.profiles.length > 0 ? v.profiles : v.profile !== void 0 ? [v.profile] : []).length < 1) ctx.addIssue({
|
|
259615
|
+
code: zod.z.ZodIssueCode.custom,
|
|
259616
|
+
message: "pass profiles[] (min 1) or legacy profile",
|
|
259617
|
+
path: ["profiles"]
|
|
259618
|
+
});
|
|
259619
|
+
});
|
|
259086
259620
|
var recordingExportCapability = {
|
|
259087
259621
|
name: "recording-export",
|
|
259088
259622
|
scope: "system",
|
|
259089
259623
|
mode: "singleton",
|
|
259090
259624
|
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, {
|
|
259625
|
+
/** Queue a render of `[fromMs,toMs)` for `deviceId`/`profiles` (legacy
|
|
259626
|
+
* singular `profile` still accepted). Fails fast when no footage covers
|
|
259627
|
+
* the range. One job per profile; returns the first queued record. */
|
|
259628
|
+
createExport: require_sleep.method(CreateExportInputSchema, ExportRecordSchema, {
|
|
259100
259629
|
kind: "mutation",
|
|
259101
259630
|
auth: "protected"
|
|
259102
259631
|
}),
|
|
@@ -262006,15 +262535,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
262006
262535
|
labels: ["probe not implemented"]
|
|
262007
262536
|
};
|
|
262008
262537
|
}
|
|
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
262538
|
async restoreDevices(savedDevices) {
|
|
262019
262539
|
await this.onRestoreDevices(savedDevices);
|
|
262020
262540
|
if (savedDevices.length > 0) this.ctx.logger.info(`Restored ${savedDevices.length} ${this.providerName} device(s)`);
|
|
@@ -262069,14 +262589,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
262069
262589
|
});
|
|
262070
262590
|
}
|
|
262071
262591
|
};
|
|
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
|
-
}));
|
|
262592
|
+
await Promise.all(topLevel.map((saved) => restoreOne(saved)));
|
|
262080
262593
|
const childRows = savedDevices.filter((s) => s.parentDeviceId !== null);
|
|
262081
262594
|
for (const saved of childRows) {
|
|
262082
262595
|
const Class = this.deviceClasses[saved.type];
|
|
@@ -267639,6 +268152,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
267639
268152
|
addonId: null,
|
|
267640
268153
|
access: "create"
|
|
267641
268154
|
},
|
|
268155
|
+
"pipelineAnalytics.reconcileFromDisk": {
|
|
268156
|
+
capName: "pipeline-analytics",
|
|
268157
|
+
capScope: "device",
|
|
268158
|
+
addonId: null,
|
|
268159
|
+
access: "create"
|
|
268160
|
+
},
|
|
267642
268161
|
"pipelineAnalytics.refreshStorageLocationsForMigration": {
|
|
267643
268162
|
capName: "pipeline-analytics",
|
|
267644
268163
|
capScope: "device",
|
|
@@ -268041,6 +268560,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
268041
268560
|
addonId: null,
|
|
268042
268561
|
access: "view"
|
|
268043
268562
|
},
|
|
268563
|
+
"pipelineOrchestrator.getReconcileFromDiskStatus": {
|
|
268564
|
+
capName: "pipeline-orchestrator",
|
|
268565
|
+
capScope: "system",
|
|
268566
|
+
addonId: null,
|
|
268567
|
+
access: "view"
|
|
268568
|
+
},
|
|
268044
268569
|
"pipelineOrchestrator.listAgentSettings": {
|
|
268045
268570
|
capName: "pipeline-orchestrator",
|
|
268046
268571
|
capScope: "system",
|
|
@@ -268065,6 +268590,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
268065
268590
|
addonId: null,
|
|
268066
268591
|
access: "create"
|
|
268067
268592
|
},
|
|
268593
|
+
"pipelineOrchestrator.reconcileFromDisk": {
|
|
268594
|
+
capName: "pipeline-orchestrator",
|
|
268595
|
+
capScope: "system",
|
|
268596
|
+
addonId: null,
|
|
268597
|
+
access: "create"
|
|
268598
|
+
},
|
|
268068
268599
|
"pipelineOrchestrator.removeAgentSettings": {
|
|
268069
268600
|
capName: "pipeline-orchestrator",
|
|
268070
268601
|
capScope: "system",
|
|
@@ -271308,6 +271839,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
271308
271839
|
form: "single",
|
|
271309
271840
|
optional: true
|
|
271310
271841
|
}],
|
|
271842
|
+
"pipelineAnalytics.reconcileFromDisk": [{
|
|
271843
|
+
name: "deviceId",
|
|
271844
|
+
form: "single",
|
|
271845
|
+
optional: false
|
|
271846
|
+
}],
|
|
271311
271847
|
"pipelineAnalytics.restageRetrainTrack": [{
|
|
271312
271848
|
name: "deviceId",
|
|
271313
271849
|
form: "single",
|
|
@@ -272926,6 +273462,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
272926
273462
|
getNodeInferenceDevices: (input) => dispatch("pipelineOrchestrator", "getNodeInferenceDevices", "query", input),
|
|
272927
273463
|
resetNodePipelineDefaults: (input) => dispatch("pipelineOrchestrator", "resetNodePipelineDefaults", "mutation", input),
|
|
272928
273464
|
getCameraStatuses: (input) => dispatch("pipelineOrchestrator", "getCameraStatuses", "query", input),
|
|
273465
|
+
reconcileFromDisk: (input) => dispatch("pipelineOrchestrator", "reconcileFromDisk", "mutation", input),
|
|
273466
|
+
getReconcileFromDiskStatus: (input) => dispatch("pipelineOrchestrator", "getReconcileFromDiskStatus", "query", input),
|
|
272929
273467
|
listTemplates: (input) => dispatch("pipelineOrchestrator", "listTemplates", "query", input),
|
|
272930
273468
|
saveTemplate: (input) => dispatch("pipelineOrchestrator", "saveTemplate", "mutation", input),
|
|
272931
273469
|
updateTemplate: (input) => dispatch("pipelineOrchestrator", "updateTemplate", "mutation", input),
|
|
@@ -272974,6 +273512,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
272974
273512
|
startStorageRebalance: (input) => dispatch("recording", "startStorageRebalance", "mutation", input)
|
|
272975
273513
|
},
|
|
272976
273514
|
recordingExport: {
|
|
273515
|
+
createExport: (input) => dispatch("recordingExport", "createExport", "mutation", input),
|
|
272977
273516
|
getExport: (input) => dispatch("recordingExport", "getExport", "query", input),
|
|
272978
273517
|
cancelExport: (input) => dispatch("recordingExport", "cancelExport", "mutation", input),
|
|
272979
273518
|
deleteExport: (input) => dispatch("recordingExport", "deleteExport", "mutation", input),
|
|
@@ -273542,6 +274081,27 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
273542
274081
|
}
|
|
273543
274082
|
return schedule.invert === true ? !inside : inside;
|
|
273544
274083
|
}
|
|
274084
|
+
var NC_SYSTEM_EVENT_FILTER_KEYS = [
|
|
274085
|
+
"deviceIds",
|
|
274086
|
+
"deviceTypes",
|
|
274087
|
+
"nodeIds",
|
|
274088
|
+
"packageNames"
|
|
274089
|
+
];
|
|
274090
|
+
function systemEventFilterApplies(kind, filter) {
|
|
274091
|
+
switch (filter) {
|
|
274092
|
+
case "deviceIds":
|
|
274093
|
+
return kind.startsWith("device-") || kind.startsWith("stream-") || kind === "detection-blind" || kind === "alarm-triggered" || kind === "export-completed";
|
|
274094
|
+
case "deviceTypes":
|
|
274095
|
+
return kind.startsWith("device-") || kind === "detection-blind";
|
|
274096
|
+
case "nodeIds":
|
|
274097
|
+
return kind.startsWith("node-") || kind === "addon-crash-loop" || kind === "addon-update-available" || kind === "addon-updated" || kind === "server-update-available" || kind === "server-updated";
|
|
274098
|
+
case "packageNames":
|
|
274099
|
+
return kind.endsWith("update-available") || kind === "addon-updated" || kind === "server-updated";
|
|
274100
|
+
}
|
|
274101
|
+
}
|
|
274102
|
+
function systemEventFilterAppliesToAnyKind(kinds, filter) {
|
|
274103
|
+
return kinds.some((kind) => systemEventFilterApplies(kind, filter));
|
|
274104
|
+
}
|
|
273545
274105
|
var TimelapseTemplateSchema = zod.z.object({
|
|
273546
274106
|
title: zod.z.string().max(500).optional(),
|
|
273547
274107
|
body: zod.z.string().max(2e3).optional()
|
|
@@ -274592,6 +275152,278 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
274592
275152
|
return rawUrl;
|
|
274593
275153
|
}
|
|
274594
275154
|
}
|
|
275155
|
+
var MB = 1024 * 1024;
|
|
275156
|
+
var DEFAULT_POOL_MEMORY_POLICY = {
|
|
275157
|
+
sampleIntervalMs: 6e4,
|
|
275158
|
+
baselineSettleSamples: 5,
|
|
275159
|
+
baselineSampleCount: 3,
|
|
275160
|
+
restartMultiple: 4,
|
|
275161
|
+
floorBytes: 1024 * MB,
|
|
275162
|
+
ceilingBytes: 3072 * MB,
|
|
275163
|
+
cooldownMs: 30 * 6e4,
|
|
275164
|
+
maxRestartsPerWindow: 6,
|
|
275165
|
+
restartWindowMs: 1440 * 6e4
|
|
275166
|
+
};
|
|
275167
|
+
function resolvePoolMemoryPolicy(env) {
|
|
275168
|
+
const num = (key, fallback, min) => {
|
|
275169
|
+
const raw = env[key];
|
|
275170
|
+
if (raw === void 0) return fallback;
|
|
275171
|
+
const parsed = Number(raw);
|
|
275172
|
+
return Number.isFinite(parsed) && parsed >= min ? parsed : fallback;
|
|
275173
|
+
};
|
|
275174
|
+
const d = DEFAULT_POOL_MEMORY_POLICY;
|
|
275175
|
+
return {
|
|
275176
|
+
sampleIntervalMs: num("CAMSTACK_POOL_MEM_INTERVAL_MS", d.sampleIntervalMs, 5e3),
|
|
275177
|
+
baselineSettleSamples: d.baselineSettleSamples,
|
|
275178
|
+
baselineSampleCount: d.baselineSampleCount,
|
|
275179
|
+
restartMultiple: num("CAMSTACK_POOL_MEM_MULTIPLE", d.restartMultiple, 1.5),
|
|
275180
|
+
floorBytes: num("CAMSTACK_POOL_MEM_FLOOR_MB", d.floorBytes / MB, 128) * MB,
|
|
275181
|
+
ceilingBytes: num("CAMSTACK_POOL_MEM_CEILING_MB", d.ceilingBytes / MB, 256) * MB,
|
|
275182
|
+
cooldownMs: num("CAMSTACK_POOL_MEM_COOLDOWN_MS", d.cooldownMs, 6e4),
|
|
275183
|
+
maxRestartsPerWindow: num("CAMSTACK_POOL_MEM_MAX_RESTARTS", d.maxRestartsPerWindow, 1),
|
|
275184
|
+
restartWindowMs: num("CAMSTACK_POOL_MEM_RESTART_WINDOW_MS", d.restartWindowMs, 6e4)
|
|
275185
|
+
};
|
|
275186
|
+
}
|
|
275187
|
+
function parseProcStatus(text) {
|
|
275188
|
+
const kb = (label) => {
|
|
275189
|
+
const match = text.match(new RegExp(`^${label}:\\s+(\\d+)\\s*kB`, "m"));
|
|
275190
|
+
return match ? Number(match[1]) * 1024 : null;
|
|
275191
|
+
};
|
|
275192
|
+
const rssBytes = kb("VmRSS");
|
|
275193
|
+
if (rssBytes === null) return null;
|
|
275194
|
+
const threadsMatch = text.match(/^Threads:\s+(\d+)/m);
|
|
275195
|
+
return {
|
|
275196
|
+
rssBytes,
|
|
275197
|
+
vmBytes: kb("VmSize") ?? 0,
|
|
275198
|
+
hwmBytes: kb("VmHWM") ?? 0,
|
|
275199
|
+
swapBytes: kb("VmSwap") ?? 0,
|
|
275200
|
+
threads: threadsMatch ? Number(threadsMatch[1]) : 0
|
|
275201
|
+
};
|
|
275202
|
+
}
|
|
275203
|
+
function initialPoolMemoryState() {
|
|
275204
|
+
return {
|
|
275205
|
+
settleSeen: 0,
|
|
275206
|
+
baselineWindow: [],
|
|
275207
|
+
baselineBytes: null,
|
|
275208
|
+
restartsAt: [],
|
|
275209
|
+
lastRestartAt: null
|
|
275210
|
+
};
|
|
275211
|
+
}
|
|
275212
|
+
function median(values) {
|
|
275213
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
275214
|
+
return sorted[Math.floor(sorted.length / 2)];
|
|
275215
|
+
}
|
|
275216
|
+
function advanceBaseline(state, rssBytes, policy) {
|
|
275217
|
+
if (state.baselineBytes !== null) return state;
|
|
275218
|
+
if (state.settleSeen < policy.baselineSettleSamples) return {
|
|
275219
|
+
...state,
|
|
275220
|
+
settleSeen: state.settleSeen + 1
|
|
275221
|
+
};
|
|
275222
|
+
const window2 = [...state.baselineWindow, rssBytes];
|
|
275223
|
+
if (window2.length < policy.baselineSampleCount) return {
|
|
275224
|
+
...state,
|
|
275225
|
+
baselineWindow: window2
|
|
275226
|
+
};
|
|
275227
|
+
return {
|
|
275228
|
+
...state,
|
|
275229
|
+
baselineWindow: window2,
|
|
275230
|
+
baselineBytes: median(window2)
|
|
275231
|
+
};
|
|
275232
|
+
}
|
|
275233
|
+
function poolMemoryThreshold(baselineBytes, policy) {
|
|
275234
|
+
if (baselineBytes === null) return policy.ceilingBytes;
|
|
275235
|
+
return Math.min(policy.ceilingBytes, Math.max(policy.floorBytes, policy.restartMultiple * baselineBytes));
|
|
275236
|
+
}
|
|
275237
|
+
function pruneRestarts(restartsAt, nowMs, policy) {
|
|
275238
|
+
return restartsAt.filter((t) => nowMs - t < policy.restartWindowMs);
|
|
275239
|
+
}
|
|
275240
|
+
function evaluatePoolMemory(state, rssBytes, nowMs, policy) {
|
|
275241
|
+
const next = advanceBaseline(state, rssBytes, policy);
|
|
275242
|
+
const thresholdBytes = poolMemoryThreshold(next.baselineBytes, policy);
|
|
275243
|
+
if (rssBytes <= thresholdBytes) return {
|
|
275244
|
+
state: next,
|
|
275245
|
+
action: next.baselineBytes === null ? "baseline-pending" : "ok",
|
|
275246
|
+
baselineBytes: next.baselineBytes,
|
|
275247
|
+
thresholdBytes
|
|
275248
|
+
};
|
|
275249
|
+
const inWindow = pruneRestarts(next.restartsAt, nowMs, policy);
|
|
275250
|
+
const pruned = {
|
|
275251
|
+
...next,
|
|
275252
|
+
restartsAt: inWindow
|
|
275253
|
+
};
|
|
275254
|
+
if (inWindow.length >= policy.maxRestartsPerWindow) return {
|
|
275255
|
+
state: pruned,
|
|
275256
|
+
action: "exhausted",
|
|
275257
|
+
baselineBytes: next.baselineBytes,
|
|
275258
|
+
thresholdBytes
|
|
275259
|
+
};
|
|
275260
|
+
if (pruned.lastRestartAt !== null && nowMs - pruned.lastRestartAt < policy.cooldownMs) return {
|
|
275261
|
+
state: pruned,
|
|
275262
|
+
action: "cooldown",
|
|
275263
|
+
baselineBytes: next.baselineBytes,
|
|
275264
|
+
thresholdBytes
|
|
275265
|
+
};
|
|
275266
|
+
return {
|
|
275267
|
+
state: pruned,
|
|
275268
|
+
action: "restart",
|
|
275269
|
+
baselineBytes: next.baselineBytes,
|
|
275270
|
+
thresholdBytes
|
|
275271
|
+
};
|
|
275272
|
+
}
|
|
275273
|
+
function commitWatchdogRestart(state, nowMs) {
|
|
275274
|
+
return {
|
|
275275
|
+
...initialPoolMemoryState(),
|
|
275276
|
+
restartsAt: [...state.restartsAt, nowMs],
|
|
275277
|
+
lastRestartAt: nowMs
|
|
275278
|
+
};
|
|
275279
|
+
}
|
|
275280
|
+
function resetPoolBaseline(state) {
|
|
275281
|
+
return {
|
|
275282
|
+
...initialPoolMemoryState(),
|
|
275283
|
+
restartsAt: state.restartsAt,
|
|
275284
|
+
lastRestartAt: state.lastRestartAt
|
|
275285
|
+
};
|
|
275286
|
+
}
|
|
275287
|
+
function pickRestartCandidate(candidates) {
|
|
275288
|
+
if (candidates.length === 0) return null;
|
|
275289
|
+
let worst = candidates[0];
|
|
275290
|
+
for (const c of candidates) if (c.rssBytes / c.thresholdBytes > worst.rssBytes / worst.thresholdBytes) worst = c;
|
|
275291
|
+
return worst.key;
|
|
275292
|
+
}
|
|
275293
|
+
var PoolMemoryWatchdog = class {
|
|
275294
|
+
pools = /* @__PURE__ */ new Map();
|
|
275295
|
+
timer = null;
|
|
275296
|
+
sweeping = false;
|
|
275297
|
+
stopped = false;
|
|
275298
|
+
opts;
|
|
275299
|
+
now;
|
|
275300
|
+
setTimer;
|
|
275301
|
+
clearTimer;
|
|
275302
|
+
constructor(opts) {
|
|
275303
|
+
this.opts = opts;
|
|
275304
|
+
this.now = opts.now ?? (() => Date.now());
|
|
275305
|
+
this.setTimer = opts.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
275306
|
+
this.clearTimer = opts.clearTimer ?? ((t) => clearTimeout(t));
|
|
275307
|
+
}
|
|
275308
|
+
start() {
|
|
275309
|
+
if (this.timer || this.stopped) return;
|
|
275310
|
+
this.arm();
|
|
275311
|
+
}
|
|
275312
|
+
stop() {
|
|
275313
|
+
this.stopped = true;
|
|
275314
|
+
if (this.timer) {
|
|
275315
|
+
this.clearTimer(this.timer);
|
|
275316
|
+
this.timer = null;
|
|
275317
|
+
}
|
|
275318
|
+
}
|
|
275319
|
+
arm() {
|
|
275320
|
+
const timer = this.setTimer(() => {
|
|
275321
|
+
this.sweep().finally(() => {
|
|
275322
|
+
if (!this.stopped) this.arm();
|
|
275323
|
+
});
|
|
275324
|
+
}, this.opts.policy.sampleIntervalMs);
|
|
275325
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
275326
|
+
this.timer = timer;
|
|
275327
|
+
}
|
|
275328
|
+
/** One sweep: sample → log every pool → restart at most one. Public so the
|
|
275329
|
+
* loop is testable without fake global timers. */
|
|
275330
|
+
async sweep() {
|
|
275331
|
+
if (this.sweeping) return;
|
|
275332
|
+
this.sweeping = true;
|
|
275333
|
+
try {
|
|
275334
|
+
await this.doSweep();
|
|
275335
|
+
} catch (err) {
|
|
275336
|
+
this.opts.log.warn("pool memory sweep failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
275337
|
+
} finally {
|
|
275338
|
+
this.sweeping = false;
|
|
275339
|
+
}
|
|
275340
|
+
}
|
|
275341
|
+
async doSweep() {
|
|
275342
|
+
const nowMs = this.now();
|
|
275343
|
+
const samples = await this.opts.sample();
|
|
275344
|
+
const candidates = [];
|
|
275345
|
+
const verdictByKey = /* @__PURE__ */ new Map();
|
|
275346
|
+
for (const t of samples) {
|
|
275347
|
+
const tracked2 = this.pools.get(t.key);
|
|
275348
|
+
let state = tracked2?.state ?? initialPoolMemoryState();
|
|
275349
|
+
if (tracked2 && tracked2.pids.length > 0 && !samePids(tracked2.pids, t.pids)) {
|
|
275350
|
+
this.opts.log.warn("pool process changed outside the watchdog \u2014 re-baselining", { meta: {
|
|
275351
|
+
poolKey: t.key,
|
|
275352
|
+
previousPids: tracked2.pids,
|
|
275353
|
+
pids: t.pids
|
|
275354
|
+
} });
|
|
275355
|
+
state = resetPoolBaseline(state);
|
|
275356
|
+
}
|
|
275357
|
+
const verdict2 = evaluatePoolMemory(state, t.rssBytes, nowMs, this.opts.policy);
|
|
275358
|
+
this.pools.set(t.key, {
|
|
275359
|
+
state: verdict2.state,
|
|
275360
|
+
pids: t.pids
|
|
275361
|
+
});
|
|
275362
|
+
verdictByKey.set(t.key, verdict2);
|
|
275363
|
+
this.opts.log.info("pool memory", { meta: {
|
|
275364
|
+
poolKey: t.key,
|
|
275365
|
+
pids: t.pids,
|
|
275366
|
+
rssMb: Math.round(t.rssBytes / MB),
|
|
275367
|
+
baselineMb: verdict2.baselineBytes === null ? null : Math.round(verdict2.baselineBytes / MB),
|
|
275368
|
+
thresholdMb: Math.round(verdict2.thresholdBytes / MB),
|
|
275369
|
+
action: verdict2.action,
|
|
275370
|
+
...t.meta
|
|
275371
|
+
} });
|
|
275372
|
+
if (verdict2.action === "restart") candidates.push({
|
|
275373
|
+
key: t.key,
|
|
275374
|
+
rssBytes: t.rssBytes,
|
|
275375
|
+
thresholdBytes: verdict2.thresholdBytes
|
|
275376
|
+
});
|
|
275377
|
+
else if (verdict2.action === "cooldown") this.opts.log.warn("pool over memory threshold but inside restart cooldown", { meta: {
|
|
275378
|
+
poolKey: t.key,
|
|
275379
|
+
rssMb: Math.round(t.rssBytes / MB),
|
|
275380
|
+
thresholdMb: Math.round(verdict2.thresholdBytes / MB),
|
|
275381
|
+
cooldownMs: this.opts.policy.cooldownMs
|
|
275382
|
+
} });
|
|
275383
|
+
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: {
|
|
275384
|
+
poolKey: t.key,
|
|
275385
|
+
rssMb: Math.round(t.rssBytes / MB),
|
|
275386
|
+
thresholdMb: Math.round(verdict2.thresholdBytes / MB),
|
|
275387
|
+
maxRestartsPerWindow: this.opts.policy.maxRestartsPerWindow,
|
|
275388
|
+
restartWindowMs: this.opts.policy.restartWindowMs
|
|
275389
|
+
} });
|
|
275390
|
+
}
|
|
275391
|
+
for (const key of [...this.pools.keys()]) if (!samples.some((t) => t.key === key)) this.pools.delete(key);
|
|
275392
|
+
const pickedKey = pickRestartCandidate(candidates);
|
|
275393
|
+
if (pickedKey === null) return;
|
|
275394
|
+
const picked = candidates.find((c) => c.key === pickedKey);
|
|
275395
|
+
const verdict = verdictByKey.get(pickedKey);
|
|
275396
|
+
const tracked = this.pools.get(pickedKey);
|
|
275397
|
+
const restartsInWindow = tracked.state.restartsAt.length + 1;
|
|
275398
|
+
this.opts.log.warn("pool memory watchdog restarting pool \u2014 RSS over threshold", { meta: {
|
|
275399
|
+
poolKey: pickedKey,
|
|
275400
|
+
pids: tracked.pids,
|
|
275401
|
+
rssMb: Math.round(picked.rssBytes / MB),
|
|
275402
|
+
thresholdMb: Math.round(picked.thresholdBytes / MB),
|
|
275403
|
+
baselineMb: verdict.baselineBytes === null ? null : Math.round(verdict.baselineBytes / MB),
|
|
275404
|
+
restartsInWindow,
|
|
275405
|
+
deferredCandidates: candidates.filter((c) => c.key !== pickedKey).map((c) => c.key)
|
|
275406
|
+
} });
|
|
275407
|
+
try {
|
|
275408
|
+
await this.opts.restart(pickedKey);
|
|
275409
|
+
this.pools.set(pickedKey, {
|
|
275410
|
+
state: commitWatchdogRestart(tracked.state, this.now()),
|
|
275411
|
+
pids: []
|
|
275412
|
+
});
|
|
275413
|
+
} catch (err) {
|
|
275414
|
+
this.opts.log.error("pool memory watchdog restart failed", { meta: {
|
|
275415
|
+
poolKey: pickedKey,
|
|
275416
|
+
error: err instanceof Error ? err.message : String(err)
|
|
275417
|
+
} });
|
|
275418
|
+
}
|
|
275419
|
+
}
|
|
275420
|
+
};
|
|
275421
|
+
function samePids(a, b) {
|
|
275422
|
+
if (a.length !== b.length) return false;
|
|
275423
|
+
const sortedA = [...a].sort((x, y) => x - y);
|
|
275424
|
+
const sortedB = [...b].sort((x, y) => x - y);
|
|
275425
|
+
return sortedA.every((v, i) => v === sortedB[i]);
|
|
275426
|
+
}
|
|
274595
275427
|
function rectsToCells(rects, gridWidth, gridHeight) {
|
|
274596
275428
|
const total = gridWidth * gridHeight;
|
|
274597
275429
|
if (total <= 0 || rects.length === 0) return Array.from({ length: Math.max(0, total) }).fill(false);
|
|
@@ -275266,6 +276098,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
275266
276098
|
exports.DEFAULT_EVENT_COLOR = DEFAULT_EVENT_COLOR;
|
|
275267
276099
|
exports.DEFAULT_FEATURES = DEFAULT_FEATURES;
|
|
275268
276100
|
exports.DEFAULT_NATIVE_LEASE_SETTINGS = DEFAULT_NATIVE_LEASE_SETTINGS;
|
|
276101
|
+
exports.DEFAULT_POOL_MEMORY_POLICY = DEFAULT_POOL_MEMORY_POLICY;
|
|
276102
|
+
exports.DEFAULT_RECORDING_PROFILES = DEFAULT_RECORDING_PROFILES;
|
|
275269
276103
|
exports.DEFAULT_RETENTION = DEFAULT_RETENTION;
|
|
275270
276104
|
exports.DEFAULT_RUNTIME_STATE_DURABILITY = require_sleep.DEFAULT_RUNTIME_STATE_DURABILITY;
|
|
275271
276105
|
exports.DEFAULT_SCRUB_THUMBNAIL_PRESET = DEFAULT_SCRUB_THUMBNAIL_PRESET;
|
|
@@ -275316,6 +276150,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
275316
276150
|
exports.DiscoveredChildStatusSchema = DiscoveredChildStatusSchema;
|
|
275317
276151
|
exports.DiscoveredDeviceSchema = DiscoveredDeviceSchema;
|
|
275318
276152
|
exports.DiscoveredTargetSchema = DiscoveredTargetSchema;
|
|
276153
|
+
exports.DiskReconcileJobSchema = DiskReconcileJobSchema;
|
|
275319
276154
|
exports.DisposerChain = require_sleep.DisposerChain;
|
|
275320
276155
|
exports.DoorbellPressEventSchema = DoorbellPressEventSchema;
|
|
275321
276156
|
exports.DoorbellStatusSchema = DoorbellStatusSchema;
|
|
@@ -275455,6 +276290,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
275455
276290
|
exports.LoginMethodContributionSchema = LoginMethodContributionSchema;
|
|
275456
276291
|
exports.LoginStageEnum = LoginStageEnum;
|
|
275457
276292
|
exports.MACRO_LABELS = MACRO_LABELS;
|
|
276293
|
+
exports.MAX_CLIP_EVENT_IDS = MAX_CLIP_EVENT_IDS;
|
|
276294
|
+
exports.MAX_CLIP_LABELS = MAX_CLIP_LABELS;
|
|
275458
276295
|
exports.MAX_CONDITION_DEPTH = MAX_CONDITION_DEPTH;
|
|
275459
276296
|
exports.MAX_CONDITION_LEAVES = MAX_CONDITION_LEAVES;
|
|
275460
276297
|
exports.MAX_EXPRESSION_AST_NODES = MAX_EXPRESSION_AST_NODES;
|
|
@@ -275546,6 +276383,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
275546
276383
|
exports.NC_HISTORY_LIMIT_MAX = NC_HISTORY_LIMIT_MAX;
|
|
275547
276384
|
exports.NC_MAX_PER_TRACK_IMMEDIATE = NC_MAX_PER_TRACK_IMMEDIATE;
|
|
275548
276385
|
exports.NC_SNOOZE_MAX_MINUTES = NC_SNOOZE_MAX_MINUTES;
|
|
276386
|
+
exports.NC_SYSTEM_EVENT_FILTER_KEYS = NC_SYSTEM_EVENT_FILTER_KEYS;
|
|
275549
276387
|
exports.NC_TAXONOMY = NC_TAXONOMY;
|
|
275550
276388
|
exports.NativeCropBboxSchema = NativeCropBboxSchema;
|
|
275551
276389
|
exports.NativeCropRefSchema = NativeCropRefSchema;
|
|
@@ -275659,6 +276497,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
275659
276497
|
exports.PipelineValidationResultSchema = PipelineValidationResultSchema;
|
|
275660
276498
|
exports.PlaceholderReasonSchema = PlaceholderReasonSchema;
|
|
275661
276499
|
exports.PolygonPointSchema = PolygonPointSchema;
|
|
276500
|
+
exports.PoolMemoryWatchdog = PoolMemoryWatchdog;
|
|
275662
276501
|
exports.PowerMeterStatusSchema = PowerMeterStatusSchema;
|
|
275663
276502
|
exports.PresenceStatusSchema = PresenceStatusSchema;
|
|
275664
276503
|
exports.PressureSensorStatusSchema = PressureSensorStatusSchema;
|
|
@@ -275913,6 +276752,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
275913
276752
|
exports.UpdateUserInputSchema = UpdateUserInputSchema;
|
|
275914
276753
|
exports.UserRecordSchema = UserRecordSchema;
|
|
275915
276754
|
exports.UserSummarySchema = UserSummarySchema;
|
|
276755
|
+
exports.VISIT_MERGE_GAP_MS = VISIT_MERGE_GAP_MS;
|
|
275916
276756
|
exports.VacuumControlStatusSchema = VacuumControlStatusSchema;
|
|
275917
276757
|
exports.VacuumStateSchema = VacuumStateSchema;
|
|
275918
276758
|
exports.ValveStateSchema = ValveStateSchema;
|
|
@@ -276025,6 +276865,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276025
276865
|
exports.collectHydratedFieldValues = require_sleep.collectHydratedFieldValues;
|
|
276026
276866
|
exports.colorCapability = colorCapability;
|
|
276027
276867
|
exports.colorForKind = colorForKind;
|
|
276868
|
+
exports.commitWatchdogRestart = commitWatchdogRestart;
|
|
276028
276869
|
exports.compileExpression = compileExpression;
|
|
276029
276870
|
exports.compileExpressionSafe = compileExpressionSafe;
|
|
276030
276871
|
exports.composeSwitchedOff = composeSwitchedOff;
|
|
@@ -276094,6 +276935,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276094
276935
|
exports.errMsg = require_err_msg.errMsg;
|
|
276095
276936
|
exports.evaluateAst = evaluateAst;
|
|
276096
276937
|
exports.evaluateExpressionSource = evaluateExpressionSource;
|
|
276938
|
+
exports.evaluatePoolMemory = evaluatePoolMemory;
|
|
276097
276939
|
exports.evaluateZoneRules = evaluateZoneRules;
|
|
276098
276940
|
exports.event = require_sleep.event;
|
|
276099
276941
|
exports.eventEmitterCapability = eventEmitterCapability;
|
|
@@ -276123,6 +276965,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276123
276965
|
exports.hydrateSchema = require_sleep.hydrateSchema;
|
|
276124
276966
|
exports.imageCapability = imageCapability;
|
|
276125
276967
|
exports.imageSettingsCapability = imageSettingsCapability;
|
|
276968
|
+
exports.initialPoolMemoryState = initialPoolMemoryState;
|
|
276126
276969
|
exports.integrationsCapability = integrationsCapability;
|
|
276127
276970
|
exports.intercomCapability = intercomCapability;
|
|
276128
276971
|
exports.invocationFromEncodeProfile = require_canonical_hash.invocationFromEncodeProfile;
|
|
@@ -276205,6 +277048,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276205
277048
|
exports.parseJsonArray = require_sleep.parseJsonArray;
|
|
276206
277049
|
exports.parseJsonObject = require_sleep.parseJsonObject;
|
|
276207
277050
|
exports.parseJsonUnknown = require_sleep.parseJsonUnknown;
|
|
277051
|
+
exports.parseProcStatus = parseProcStatus;
|
|
276208
277052
|
exports.parseProfileBrokerId = require_sleep.parseProfileBrokerId;
|
|
276209
277053
|
exports.parseStreamParamsFormPatch = parseStreamParamsFormPatch;
|
|
276210
277054
|
exports.patchAudio = patchAudio;
|
|
@@ -276213,6 +277057,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276213
277057
|
exports.pickDetailCropConvention = pickDetailCropConvention;
|
|
276214
277058
|
exports.pickNativeLeaseOverride = pickNativeLeaseOverride;
|
|
276215
277059
|
exports.pickPreferredRtspEntry = pickPreferredRtspEntry;
|
|
277060
|
+
exports.pickRestartCandidate = pickRestartCandidate;
|
|
276216
277061
|
exports.pickVideoEncoder = require_canonical_hash.pickVideoEncoder;
|
|
276217
277062
|
exports.pickerForCondition = pickerForCondition;
|
|
276218
277063
|
exports.pipelineAnalyticsCapability = pipelineAnalyticsCapability;
|
|
@@ -276221,6 +277066,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276221
277066
|
exports.pipelineRunnerCapability = pipelineRunnerCapability;
|
|
276222
277067
|
exports.plateGalleryCapability = plateGalleryCapability;
|
|
276223
277068
|
exports.platformProbeCapability = platformProbeCapability;
|
|
277069
|
+
exports.poolMemoryThreshold = poolMemoryThreshold;
|
|
276224
277070
|
exports.powerMeterCapability = powerMeterCapability;
|
|
276225
277071
|
exports.prepareNotification = prepareNotification;
|
|
276226
277072
|
exports.presenceCapability = presenceCapability;
|
|
@@ -276242,6 +277088,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276242
277088
|
exports.recordingExportCapability = recordingExportCapability;
|
|
276243
277089
|
exports.rectsToCells = rectsToCells;
|
|
276244
277090
|
exports.requiresPython = requiresPython;
|
|
277091
|
+
exports.resetPoolBaseline = resetPoolBaseline;
|
|
276245
277092
|
exports.resolveAddonExecution = resolveAddonExecution;
|
|
276246
277093
|
exports.resolveAddonGroup = resolveAddonGroup;
|
|
276247
277094
|
exports.resolveAddonPlacement = resolveAddonPlacement;
|
|
@@ -276255,6 +277102,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276255
277102
|
exports.resolveHydratedFieldValue = require_sleep.resolveHydratedFieldValue;
|
|
276256
277103
|
exports.resolveModelFormat = resolveModelFormat;
|
|
276257
277104
|
exports.resolveMutate = resolveMutate;
|
|
277105
|
+
exports.resolvePoolMemoryPolicy = resolvePoolMemoryPolicy;
|
|
277106
|
+
exports.resolveRecordingProfiles = resolveRecordingProfiles;
|
|
276258
277107
|
exports.resolveRunnerId = resolveRunnerId;
|
|
276259
277108
|
exports.resolveScrubThumbnailGeometry = resolveScrubThumbnailGeometry;
|
|
276260
277109
|
exports.resolveVariantModelId = resolveVariantModelId;
|
|
@@ -276299,6 +277148,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
276299
277148
|
exports.switchedOffIds = switchedOffIds;
|
|
276300
277149
|
exports.synthesizeSourceInfo = synthesizeSourceInfo;
|
|
276301
277150
|
exports.systemCapability = systemCapability;
|
|
277151
|
+
exports.systemEventFilterApplies = systemEventFilterApplies;
|
|
277152
|
+
exports.systemEventFilterAppliesToAnyKind = systemEventFilterAppliesToAnyKind;
|
|
276302
277153
|
exports.tamperCapability = tamperCapability;
|
|
276303
277154
|
exports.taskLogEntrySchema = taskLogEntrySchema;
|
|
276304
277155
|
exports.taskPhaseSchema = taskPhaseSchema;
|
|
@@ -276454,6 +277305,314 @@ var require_agent_config = __commonJS({
|
|
|
276454
277305
|
}
|
|
276455
277306
|
});
|
|
276456
277307
|
|
|
277308
|
+
// ../../server/backend/dist/core/server-update/system-ensure-prebuilds.js
|
|
277309
|
+
var require_system_ensure_prebuilds = __commonJS({
|
|
277310
|
+
"../../server/backend/dist/core/server-update/system-ensure-prebuilds.js"(exports) {
|
|
277311
|
+
"use strict";
|
|
277312
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
277313
|
+
exports.buildSystemEnsureNativePrebuilds = buildSystemEnsureNativePrebuilds;
|
|
277314
|
+
var system_1 = require_dist3();
|
|
277315
|
+
function buildSystemEnsureNativePrebuilds(logger) {
|
|
277316
|
+
return async (closureDir) => {
|
|
277317
|
+
await (0, system_1.ensureNativePrebuilds)(closureDir, { logger });
|
|
277318
|
+
};
|
|
277319
|
+
}
|
|
277320
|
+
}
|
|
277321
|
+
});
|
|
277322
|
+
|
|
277323
|
+
// ../../server/backend/dist/core/server-update/system-exec-npm.js
|
|
277324
|
+
var require_system_exec_npm = __commonJS({
|
|
277325
|
+
"../../server/backend/dist/core/server-update/system-exec-npm.js"(exports) {
|
|
277326
|
+
"use strict";
|
|
277327
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
277328
|
+
exports.buildSystemExecNpm = buildSystemExecNpm;
|
|
277329
|
+
var system_1 = require_dist3();
|
|
277330
|
+
function buildSystemExecNpm(options, runNpmFn = system_1.runNpm) {
|
|
277331
|
+
return async (args, opts) => {
|
|
277332
|
+
const { stdout } = await runNpmFn(args, {
|
|
277333
|
+
cacheDir: options.cacheDir,
|
|
277334
|
+
registry: options.registry,
|
|
277335
|
+
logger: options.logger,
|
|
277336
|
+
...opts.cwd !== void 0 ? { cwd: opts.cwd } : {},
|
|
277337
|
+
timeout: opts.timeoutMs
|
|
277338
|
+
});
|
|
277339
|
+
return { stdout };
|
|
277340
|
+
};
|
|
277341
|
+
}
|
|
277342
|
+
}
|
|
277343
|
+
});
|
|
277344
|
+
|
|
277345
|
+
// ../../server/backend/dist/core/update-availability-emitter.js
|
|
277346
|
+
var require_update_availability_emitter = __commonJS({
|
|
277347
|
+
"../../server/backend/dist/core/update-availability-emitter.js"(exports) {
|
|
277348
|
+
"use strict";
|
|
277349
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
277350
|
+
exports.UpdateAvailabilityEmitter = void 0;
|
|
277351
|
+
var types_1 = require_dist9();
|
|
277352
|
+
var UpdateAvailabilityEmitter = class {
|
|
277353
|
+
eventBus;
|
|
277354
|
+
source;
|
|
277355
|
+
store;
|
|
277356
|
+
latestByKey = /* @__PURE__ */ new Map();
|
|
277357
|
+
held = /* @__PURE__ */ new Map();
|
|
277358
|
+
/** Set when in-memory state diverged from what the store last saw. */
|
|
277359
|
+
dirty = false;
|
|
277360
|
+
constructor(eventBus, source, store) {
|
|
277361
|
+
this.eventBus = eventBus;
|
|
277362
|
+
this.source = source;
|
|
277363
|
+
this.store = store;
|
|
277364
|
+
const persisted = store?.load() ?? null;
|
|
277365
|
+
if (persisted === null)
|
|
277366
|
+
return;
|
|
277367
|
+
for (const [key, version] of Object.entries(persisted.latestByKey)) {
|
|
277368
|
+
this.latestByKey.set(key, version);
|
|
277369
|
+
}
|
|
277370
|
+
for (const [key, candidate] of Object.entries(persisted.held)) {
|
|
277371
|
+
this.held.set(key, candidate);
|
|
277372
|
+
}
|
|
277373
|
+
}
|
|
277374
|
+
publishSnapshot(target, candidates, nodeId) {
|
|
277375
|
+
const scope = nodeId ?? "hub";
|
|
277376
|
+
const present = /* @__PURE__ */ new Set();
|
|
277377
|
+
let latestChanged = false;
|
|
277378
|
+
for (const candidate of candidates) {
|
|
277379
|
+
if (candidate.target !== target)
|
|
277380
|
+
continue;
|
|
277381
|
+
const stamped = stampNode(candidate, nodeId);
|
|
277382
|
+
const key = this.key(stamped);
|
|
277383
|
+
present.add(key);
|
|
277384
|
+
if (this.note(stamped))
|
|
277385
|
+
latestChanged = true;
|
|
277386
|
+
}
|
|
277387
|
+
for (const key of [...this.held.keys()]) {
|
|
277388
|
+
if (!key.startsWith(`${target}:${scope}:`))
|
|
277389
|
+
continue;
|
|
277390
|
+
if (present.has(key))
|
|
277391
|
+
continue;
|
|
277392
|
+
this.held.delete(key);
|
|
277393
|
+
this.latestByKey.delete(key);
|
|
277394
|
+
this.dirty = true;
|
|
277395
|
+
}
|
|
277396
|
+
this.persistIfDirty();
|
|
277397
|
+
if (!latestChanged)
|
|
277398
|
+
return;
|
|
277399
|
+
this.emitList(target);
|
|
277400
|
+
}
|
|
277401
|
+
/** Publish a partial candidate set without treating omissions as up-to-date. */
|
|
277402
|
+
publishCandidates(candidates) {
|
|
277403
|
+
let latestChanged = false;
|
|
277404
|
+
const targets = /* @__PURE__ */ new Set();
|
|
277405
|
+
for (const candidate of candidates) {
|
|
277406
|
+
targets.add(candidate.target);
|
|
277407
|
+
if (this.note(candidate))
|
|
277408
|
+
latestChanged = true;
|
|
277409
|
+
}
|
|
277410
|
+
this.persistIfDirty();
|
|
277411
|
+
if (!latestChanged)
|
|
277412
|
+
return;
|
|
277413
|
+
for (const target of targets)
|
|
277414
|
+
this.emitList(target);
|
|
277415
|
+
}
|
|
277416
|
+
note(candidate) {
|
|
277417
|
+
const key = this.key(candidate);
|
|
277418
|
+
const previous = this.latestByKey.get(key);
|
|
277419
|
+
const previousHeld = this.held.get(key);
|
|
277420
|
+
if (previousHeld === void 0 || previousHeld.currentVersion !== candidate.currentVersion || previousHeld.latestVersion !== candidate.latestVersion || previousHeld.nodeId !== candidate.nodeId) {
|
|
277421
|
+
this.dirty = true;
|
|
277422
|
+
}
|
|
277423
|
+
this.held.set(key, candidate);
|
|
277424
|
+
this.latestByKey.set(key, candidate.latestVersion);
|
|
277425
|
+
return previous !== candidate.latestVersion;
|
|
277426
|
+
}
|
|
277427
|
+
/**
|
|
277428
|
+
* Flush to the durable store. Called BEFORE the emit so a crash between the
|
|
277429
|
+
* two costs a duplicate announcement, never a silent one.
|
|
277430
|
+
*/
|
|
277431
|
+
persistIfDirty() {
|
|
277432
|
+
if (!this.dirty)
|
|
277433
|
+
return;
|
|
277434
|
+
this.dirty = false;
|
|
277435
|
+
this.store?.save({
|
|
277436
|
+
latestByKey: Object.fromEntries(this.latestByKey),
|
|
277437
|
+
held: Object.fromEntries(this.held)
|
|
277438
|
+
});
|
|
277439
|
+
}
|
|
277440
|
+
emitList(target) {
|
|
277441
|
+
const list = [...this.held.values()].filter((candidate) => candidate.target === target);
|
|
277442
|
+
if (list.length === 0)
|
|
277443
|
+
return;
|
|
277444
|
+
const packages = list.map((candidate) => ({
|
|
277445
|
+
packageName: candidate.packageName,
|
|
277446
|
+
currentVersion: candidate.currentVersion,
|
|
277447
|
+
latestVersion: candidate.latestVersion,
|
|
277448
|
+
...candidate.nodeId !== void 0 ? { nodeId: candidate.nodeId } : {}
|
|
277449
|
+
}));
|
|
277450
|
+
const nodeIds = unique(packages.map((pkg) => pkg.nodeId).filter((id) => id !== void 0));
|
|
277451
|
+
const head = list[0];
|
|
277452
|
+
this.eventBus.emit({
|
|
277453
|
+
id: `update.available:${target}:${packages.map((pkg) => `${pkg.nodeId ?? "hub"}:${pkg.packageName}@${pkg.latestVersion}`).toSorted().join(",")}`,
|
|
277454
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
277455
|
+
source: this.source,
|
|
277456
|
+
category: types_1.EventCategory.UpdateAvailable,
|
|
277457
|
+
data: {
|
|
277458
|
+
target,
|
|
277459
|
+
packageName: head.packageName,
|
|
277460
|
+
currentVersion: head.currentVersion,
|
|
277461
|
+
latestVersion: head.latestVersion,
|
|
277462
|
+
packages,
|
|
277463
|
+
...head.nodeId !== void 0 ? { nodeId: head.nodeId } : {},
|
|
277464
|
+
...nodeIds.length > 0 ? { nodeIds } : {}
|
|
277465
|
+
}
|
|
277466
|
+
});
|
|
277467
|
+
}
|
|
277468
|
+
key(candidate) {
|
|
277469
|
+
return `${candidate.target}:${candidate.nodeId ?? "hub"}:${candidate.packageName}`;
|
|
277470
|
+
}
|
|
277471
|
+
};
|
|
277472
|
+
exports.UpdateAvailabilityEmitter = UpdateAvailabilityEmitter;
|
|
277473
|
+
function stampNode(candidate, nodeId) {
|
|
277474
|
+
if (nodeId === void 0 || candidate.nodeId !== void 0)
|
|
277475
|
+
return candidate;
|
|
277476
|
+
return { ...candidate, nodeId };
|
|
277477
|
+
}
|
|
277478
|
+
function unique(values) {
|
|
277479
|
+
return [...new Set(values)];
|
|
277480
|
+
}
|
|
277481
|
+
}
|
|
277482
|
+
});
|
|
277483
|
+
|
|
277484
|
+
// ../../server/backend/dist/core/update-availability-store.js
|
|
277485
|
+
var require_update_availability_store = __commonJS({
|
|
277486
|
+
"../../server/backend/dist/core/update-availability-store.js"(exports) {
|
|
277487
|
+
"use strict";
|
|
277488
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
277489
|
+
if (k2 === void 0) k2 = k;
|
|
277490
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
277491
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
277492
|
+
desc = { enumerable: true, get: function() {
|
|
277493
|
+
return m[k];
|
|
277494
|
+
} };
|
|
277495
|
+
}
|
|
277496
|
+
Object.defineProperty(o, k2, desc);
|
|
277497
|
+
}) : (function(o, m, k, k2) {
|
|
277498
|
+
if (k2 === void 0) k2 = k;
|
|
277499
|
+
o[k2] = m[k];
|
|
277500
|
+
}));
|
|
277501
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
277502
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
277503
|
+
}) : function(o, v) {
|
|
277504
|
+
o["default"] = v;
|
|
277505
|
+
});
|
|
277506
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() {
|
|
277507
|
+
var ownKeys = function(o) {
|
|
277508
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
277509
|
+
var ar = [];
|
|
277510
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
277511
|
+
return ar;
|
|
277512
|
+
};
|
|
277513
|
+
return ownKeys(o);
|
|
277514
|
+
};
|
|
277515
|
+
return function(mod) {
|
|
277516
|
+
if (mod && mod.__esModule) return mod;
|
|
277517
|
+
var result = {};
|
|
277518
|
+
if (mod != null) {
|
|
277519
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
277520
|
+
}
|
|
277521
|
+
__setModuleDefault(result, mod);
|
|
277522
|
+
return result;
|
|
277523
|
+
};
|
|
277524
|
+
})();
|
|
277525
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
277526
|
+
exports.FileUpdateAvailabilityStore = exports.UPDATE_AVAILABILITY_DIR = void 0;
|
|
277527
|
+
var fs = __importStar(__require("fs"));
|
|
277528
|
+
var path = __importStar(__require("path"));
|
|
277529
|
+
var types_1 = require_dist9();
|
|
277530
|
+
exports.UPDATE_AVAILABILITY_DIR = "update-availability";
|
|
277531
|
+
var FileUpdateAvailabilityStore = class {
|
|
277532
|
+
scopeId;
|
|
277533
|
+
logger;
|
|
277534
|
+
filePath;
|
|
277535
|
+
constructor(dataDir, scopeId, logger) {
|
|
277536
|
+
this.scopeId = scopeId;
|
|
277537
|
+
this.logger = logger;
|
|
277538
|
+
this.filePath = path.join(dataDir, exports.UPDATE_AVAILABILITY_DIR, `${scopeId}.json`);
|
|
277539
|
+
}
|
|
277540
|
+
load() {
|
|
277541
|
+
try {
|
|
277542
|
+
if (!fs.existsSync(this.filePath))
|
|
277543
|
+
return null;
|
|
277544
|
+
const parsed = JSON.parse(fs.readFileSync(this.filePath, "utf-8"));
|
|
277545
|
+
return parseState(parsed);
|
|
277546
|
+
} catch (error) {
|
|
277547
|
+
this.logger.warn("Update-availability state unreadable; starting from empty", {
|
|
277548
|
+
meta: { scopeId: this.scopeId, path: this.filePath, error: (0, types_1.errMsg)(error) }
|
|
277549
|
+
});
|
|
277550
|
+
return null;
|
|
277551
|
+
}
|
|
277552
|
+
}
|
|
277553
|
+
save(state) {
|
|
277554
|
+
try {
|
|
277555
|
+
fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
|
277556
|
+
const tmp = `${this.filePath}.tmp`;
|
|
277557
|
+
fs.writeFileSync(tmp, JSON.stringify(state, null, 2));
|
|
277558
|
+
fs.renameSync(tmp, this.filePath);
|
|
277559
|
+
} catch (error) {
|
|
277560
|
+
this.logger.warn("Failed to persist update-availability state", {
|
|
277561
|
+
meta: { scopeId: this.scopeId, path: this.filePath, error: (0, types_1.errMsg)(error) }
|
|
277562
|
+
});
|
|
277563
|
+
}
|
|
277564
|
+
}
|
|
277565
|
+
};
|
|
277566
|
+
exports.FileUpdateAvailabilityStore = FileUpdateAvailabilityStore;
|
|
277567
|
+
function parseState(raw) {
|
|
277568
|
+
if (raw === null || typeof raw !== "object")
|
|
277569
|
+
return null;
|
|
277570
|
+
const latestRaw = Reflect.get(raw, "latestByKey");
|
|
277571
|
+
const heldRaw = Reflect.get(raw, "held");
|
|
277572
|
+
if (latestRaw === null || typeof latestRaw !== "object")
|
|
277573
|
+
return null;
|
|
277574
|
+
if (heldRaw === null || typeof heldRaw !== "object")
|
|
277575
|
+
return null;
|
|
277576
|
+
const latestByKey = {};
|
|
277577
|
+
for (const [key, value] of Object.entries(latestRaw)) {
|
|
277578
|
+
if (typeof value === "string")
|
|
277579
|
+
latestByKey[key] = value;
|
|
277580
|
+
}
|
|
277581
|
+
const held = {};
|
|
277582
|
+
for (const [key, value] of Object.entries(heldRaw)) {
|
|
277583
|
+
const candidate = parseCandidate(value);
|
|
277584
|
+
if (candidate !== null)
|
|
277585
|
+
held[key] = candidate;
|
|
277586
|
+
}
|
|
277587
|
+
return { latestByKey, held };
|
|
277588
|
+
}
|
|
277589
|
+
function parseCandidate(raw) {
|
|
277590
|
+
if (raw === null || typeof raw !== "object")
|
|
277591
|
+
return null;
|
|
277592
|
+
const target = Reflect.get(raw, "target");
|
|
277593
|
+
const packageName = Reflect.get(raw, "packageName");
|
|
277594
|
+
const currentVersion = Reflect.get(raw, "currentVersion");
|
|
277595
|
+
const latestVersion = Reflect.get(raw, "latestVersion");
|
|
277596
|
+
const nodeId = Reflect.get(raw, "nodeId");
|
|
277597
|
+
if (target !== "addon" && target !== "server")
|
|
277598
|
+
return null;
|
|
277599
|
+
if (typeof packageName !== "string")
|
|
277600
|
+
return null;
|
|
277601
|
+
if (typeof currentVersion !== "string")
|
|
277602
|
+
return null;
|
|
277603
|
+
if (typeof latestVersion !== "string")
|
|
277604
|
+
return null;
|
|
277605
|
+
return {
|
|
277606
|
+
target,
|
|
277607
|
+
packageName,
|
|
277608
|
+
currentVersion,
|
|
277609
|
+
latestVersion,
|
|
277610
|
+
...typeof nodeId === "string" ? { nodeId } : {}
|
|
277611
|
+
};
|
|
277612
|
+
}
|
|
277613
|
+
}
|
|
277614
|
+
});
|
|
277615
|
+
|
|
276457
277616
|
// ../../server/backend/dist/server-root/index.js
|
|
276458
277617
|
var require_server_root = __commonJS({
|
|
276459
277618
|
"../../server/backend/dist/server-root/index.js"(exports, module) {
|
|
@@ -277958,43 +279117,6 @@ var require_server_root = __commonJS({
|
|
|
277958
279117
|
}
|
|
277959
279118
|
});
|
|
277960
279119
|
|
|
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
279120
|
// ../../server/backend/dist/agent/agent-update-service.js
|
|
277999
279121
|
var require_agent_update_service = __commonJS({
|
|
278000
279122
|
"../../server/backend/dist/agent/agent-update-service.js"(exports) {
|
|
@@ -278042,11 +279164,16 @@ var require_agent_update_service = __commonJS({
|
|
|
278042
279164
|
exports.resolveAgentPackageJsonPath = resolveAgentPackageJsonPath;
|
|
278043
279165
|
exports.scheduleAgentRestart = scheduleAgentRestart;
|
|
278044
279166
|
exports.agentRuntimeManifestEntry = agentRuntimeManifestEntry;
|
|
279167
|
+
var node_crypto_1 = __require("crypto");
|
|
278045
279168
|
var fs = __importStar(__require("fs"));
|
|
278046
279169
|
var path = __importStar(__require("path"));
|
|
278047
|
-
var
|
|
278048
|
-
var
|
|
279170
|
+
var system_1 = require_dist3();
|
|
279171
|
+
var types_1 = require_dist9();
|
|
278049
279172
|
var system_ensure_prebuilds_js_1 = require_system_ensure_prebuilds();
|
|
279173
|
+
var system_exec_npm_js_1 = require_system_exec_npm();
|
|
279174
|
+
var update_availability_emitter_js_1 = require_update_availability_emitter();
|
|
279175
|
+
var update_availability_store_js_1 = require_update_availability_store();
|
|
279176
|
+
var index_js_1 = require_server_root();
|
|
278050
279177
|
exports.AGENT_RUNTIME_ADDON_ID = "agent-runtime";
|
|
278051
279178
|
exports.AGENT_RESTART_GRACE_MS = 2e3;
|
|
278052
279179
|
function isRunningInContainer(existsSyncFn) {
|
|
@@ -278091,7 +279218,14 @@ var require_agent_update_service = __commonJS({
|
|
|
278091
279218
|
grace.unref();
|
|
278092
279219
|
}
|
|
278093
279220
|
var AgentUpdateService = class extends index_js_1.RootUpdateService {
|
|
279221
|
+
updateAvailability;
|
|
279222
|
+
eventBus;
|
|
279223
|
+
nodeId;
|
|
279224
|
+
agentDataDir;
|
|
279225
|
+
/** The base class keeps `logger` private; hold our own for the store. */
|
|
279226
|
+
agentLogger;
|
|
278094
279227
|
constructor(options) {
|
|
279228
|
+
const restartAgent = options.restartAgent ?? ((requestedBy) => scheduleAgentRestart(options.logger, requestedBy, options.dataDir));
|
|
278095
279229
|
super({
|
|
278096
279230
|
spec: index_js_1.HUB_ROOT_SPEC,
|
|
278097
279231
|
// The agent's on-disk root IS `@camstack/server` (booted via
|
|
@@ -278103,7 +279237,10 @@ var require_agent_update_service = __commonJS({
|
|
|
278103
279237
|
seedDir: "CAMSTACK_SEED_SERVER_DIR"
|
|
278104
279238
|
},
|
|
278105
279239
|
logger: options.logger,
|
|
278106
|
-
restartServer:
|
|
279240
|
+
restartServer: (requestedBy) => {
|
|
279241
|
+
writeAgentRestartMarker(options.dataDir, requestedBy);
|
|
279242
|
+
restartAgent(requestedBy);
|
|
279243
|
+
},
|
|
278107
279244
|
dataDir: options.dataDir,
|
|
278108
279245
|
runningPackageJsonPath: options.runningPackageJsonPath ?? resolveAgentPackageJsonPath(__dirname),
|
|
278109
279246
|
workspaceProbeDir: __dirname,
|
|
@@ -278121,9 +279258,79 @@ var require_agent_update_service = __commonJS({
|
|
|
278121
279258
|
env: options.env,
|
|
278122
279259
|
now: options.now
|
|
278123
279260
|
});
|
|
279261
|
+
this.nodeId = options.nodeId ?? process.env["CAMSTACK_NODE_ID"] ?? "agent";
|
|
279262
|
+
this.agentDataDir = options.dataDir;
|
|
279263
|
+
this.agentLogger = options.logger;
|
|
279264
|
+
this.eventBus = options.eventBus ?? null;
|
|
279265
|
+
this.updateAvailability = options.eventBus !== void 0 ? new update_availability_emitter_js_1.UpdateAvailabilityEmitter(options.eventBus, { type: "core", id: "agent-update-service" }, options.updateAvailabilityStore) : null;
|
|
279266
|
+
}
|
|
279267
|
+
/**
|
|
279268
|
+
* Bind the agent's real event bus once the mesh is up. The dedup state is
|
|
279269
|
+
* persisted under the agent's data dir from here on — an agent restarts on
|
|
279270
|
+
* every root update, which is exactly when its availability list is longest.
|
|
279271
|
+
*/
|
|
279272
|
+
bindAvailability(eventBus, nodeId) {
|
|
279273
|
+
this.eventBus = eventBus;
|
|
279274
|
+
this.nodeId = nodeId;
|
|
279275
|
+
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));
|
|
279276
|
+
}
|
|
279277
|
+
async checkServerUpdate() {
|
|
279278
|
+
const result = await super.checkServerUpdate();
|
|
279279
|
+
if (result.error !== null)
|
|
279280
|
+
return result;
|
|
279281
|
+
this.updateAvailability?.publishSnapshot("server", result.updateAvailable && result.runningVersion !== null && result.latestVersion !== null ? [
|
|
279282
|
+
{
|
|
279283
|
+
target: "server",
|
|
279284
|
+
packageName: result.packageName,
|
|
279285
|
+
currentVersion: result.runningVersion,
|
|
279286
|
+
latestVersion: result.latestVersion,
|
|
279287
|
+
nodeId: this.nodeId
|
|
279288
|
+
}
|
|
279289
|
+
] : [], this.nodeId);
|
|
279290
|
+
return result;
|
|
279291
|
+
}
|
|
279292
|
+
/** Consume `.restart-pending` and emit `system.restart-completed` if present. */
|
|
279293
|
+
emitRestartCompletedIfPending() {
|
|
279294
|
+
const bus = this.eventBus;
|
|
279295
|
+
if (bus === null)
|
|
279296
|
+
return;
|
|
279297
|
+
const marker = (0, system_1.readPendingRestart)(this.agentDataDir);
|
|
279298
|
+
if (marker === null)
|
|
279299
|
+
return;
|
|
279300
|
+
const payload = {
|
|
279301
|
+
kind: marker.kind,
|
|
279302
|
+
requestedAt: marker.requestedAt,
|
|
279303
|
+
nodeId: this.nodeId,
|
|
279304
|
+
...marker.packageName !== void 0 ? { packageName: marker.packageName } : {},
|
|
279305
|
+
...marker.fromVersion !== void 0 ? { fromVersion: marker.fromVersion } : {},
|
|
279306
|
+
...marker.toVersion !== void 0 ? { toVersion: marker.toVersion } : {},
|
|
279307
|
+
...marker.requestedBy !== void 0 ? { requestedBy: marker.requestedBy } : {}
|
|
279308
|
+
};
|
|
279309
|
+
bus.emit({
|
|
279310
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
279311
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
279312
|
+
source: { type: "core", id: "agent-update-service" },
|
|
279313
|
+
category: types_1.EventCategory.SystemRestartCompleted,
|
|
279314
|
+
data: payload
|
|
279315
|
+
});
|
|
278124
279316
|
}
|
|
278125
279317
|
};
|
|
278126
279318
|
exports.AgentUpdateService = AgentUpdateService;
|
|
279319
|
+
function writeAgentRestartMarker(dataDir, requestedBy) {
|
|
279320
|
+
const toVersion = /@([^@\s]+)$/.exec(requestedBy)?.[1];
|
|
279321
|
+
try {
|
|
279322
|
+
(0, system_1.writePendingRestart)(dataDir, {
|
|
279323
|
+
kind: requestedBy.startsWith("server-update:") ? "framework-update" : "manual",
|
|
279324
|
+
requestedAt: Date.now(),
|
|
279325
|
+
requestedBy,
|
|
279326
|
+
...requestedBy.startsWith("server-update:") ? {
|
|
279327
|
+
packageName: "@camstack/server",
|
|
279328
|
+
...toVersion !== void 0 ? { toVersion } : {}
|
|
279329
|
+
} : {}
|
|
279330
|
+
});
|
|
279331
|
+
} catch {
|
|
279332
|
+
}
|
|
279333
|
+
}
|
|
278127
279334
|
function agentRuntimeManifestEntry() {
|
|
278128
279335
|
return { addonId: exports.AGENT_RUNTIME_ADDON_ID, capabilities: ["server-management"] };
|
|
278129
279336
|
}
|
|
@@ -370765,6 +371972,7 @@ var require_main2 = __commonJS({
|
|
|
370765
371972
|
agentBootConfirmed = true;
|
|
370766
371973
|
try {
|
|
370767
371974
|
agentUpdateService.confirmBootHealthy();
|
|
371975
|
+
agentUpdateService.emitRestartCompletedIfPending();
|
|
370768
371976
|
} catch (err) {
|
|
370769
371977
|
consoleLogger.warn(`agent root boot confirmation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
370770
371978
|
}
|
|
@@ -370944,9 +372152,10 @@ var require_main2 = __commonJS({
|
|
|
370944
372152
|
return;
|
|
370945
372153
|
reconcileHubUrlFromRegistry();
|
|
370946
372154
|
});
|
|
372155
|
+
const agentBrokerEventBus = (0, system_1.getBrokerEventBus)(broker);
|
|
372156
|
+
agentUpdateService.bindAvailability(agentBrokerEventBus, broker.nodeID);
|
|
370947
372157
|
let udsEventBridgeDispose = null;
|
|
370948
372158
|
if (agentUdsRegistry !== void 0) {
|
|
370949
|
-
const agentBrokerEventBus = (0, system_1.getBrokerEventBus)(broker);
|
|
370950
372159
|
udsEventBridgeDispose = (0, system_1.createUdsEventBridge)({
|
|
370951
372160
|
registry: agentUdsRegistry,
|
|
370952
372161
|
parentBus: agentBrokerEventBus,
|
|
@@ -386589,61 +387798,126 @@ var require_ws3 = __commonJS({
|
|
|
386589
387798
|
}
|
|
386590
387799
|
});
|
|
386591
387800
|
|
|
386592
|
-
// ../../server/backend/dist/core/update-
|
|
386593
|
-
var
|
|
386594
|
-
"../../server/backend/dist/core/update-
|
|
387801
|
+
// ../../server/backend/dist/core/updates/update-check-scheduler.js
|
|
387802
|
+
var require_update_check_scheduler = __commonJS({
|
|
387803
|
+
"../../server/backend/dist/core/updates/update-check-scheduler.js"(exports) {
|
|
386595
387804
|
"use strict";
|
|
386596
387805
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
386597
|
-
exports.
|
|
387806
|
+
exports.UpdateCheckScheduler = exports.MAX_UPDATE_CHECK_INTERVAL_SECONDS = exports.MIN_UPDATE_CHECK_INTERVAL_SECONDS = exports.DEFAULT_UPDATE_CHECK_INTERVAL_SECONDS = void 0;
|
|
387807
|
+
exports.clampUpdateCheckInterval = clampUpdateCheckInterval;
|
|
386598
387808
|
var types_1 = require_dist9();
|
|
386599
|
-
|
|
386600
|
-
|
|
386601
|
-
|
|
386602
|
-
|
|
386603
|
-
|
|
386604
|
-
|
|
386605
|
-
|
|
387809
|
+
exports.DEFAULT_UPDATE_CHECK_INTERVAL_SECONDS = 6 * 60 * 60;
|
|
387810
|
+
exports.MIN_UPDATE_CHECK_INTERVAL_SECONDS = 15 * 60;
|
|
387811
|
+
exports.MAX_UPDATE_CHECK_INTERVAL_SECONDS = 7 * 24 * 60 * 60;
|
|
387812
|
+
var DEFAULT_INITIAL_DELAY_MS = 9e4;
|
|
387813
|
+
function clampUpdateCheckInterval(seconds) {
|
|
387814
|
+
if (!Number.isFinite(seconds))
|
|
387815
|
+
return exports.DEFAULT_UPDATE_CHECK_INTERVAL_SECONDS;
|
|
387816
|
+
const whole = Math.floor(seconds);
|
|
387817
|
+
if (whole < exports.MIN_UPDATE_CHECK_INTERVAL_SECONDS)
|
|
387818
|
+
return exports.MIN_UPDATE_CHECK_INTERVAL_SECONDS;
|
|
387819
|
+
if (whole > exports.MAX_UPDATE_CHECK_INTERVAL_SECONDS)
|
|
387820
|
+
return exports.MAX_UPDATE_CHECK_INTERVAL_SECONDS;
|
|
387821
|
+
return whole;
|
|
387822
|
+
}
|
|
387823
|
+
var UpdateCheckScheduler = class {
|
|
387824
|
+
logger;
|
|
387825
|
+
targets;
|
|
387826
|
+
getIntervalSeconds;
|
|
387827
|
+
initialDelayMs;
|
|
387828
|
+
timer = null;
|
|
387829
|
+
bootTimer = null;
|
|
387830
|
+
intervalSeconds = exports.DEFAULT_UPDATE_CHECK_INTERVAL_SECONDS;
|
|
387831
|
+
sweeping = false;
|
|
387832
|
+
constructor(options) {
|
|
387833
|
+
this.logger = options.logger;
|
|
387834
|
+
this.targets = options.targets;
|
|
387835
|
+
this.getIntervalSeconds = options.getIntervalSeconds;
|
|
387836
|
+
this.initialDelayMs = options.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;
|
|
386606
387837
|
}
|
|
386607
|
-
|
|
386608
|
-
|
|
386609
|
-
|
|
386610
|
-
|
|
386611
|
-
|
|
386612
|
-
|
|
386613
|
-
|
|
386614
|
-
|
|
386615
|
-
this.
|
|
387838
|
+
/** Interval currently armed, after clamping. Exposed for assertions + logs. */
|
|
387839
|
+
currentIntervalSeconds() {
|
|
387840
|
+
return this.intervalSeconds;
|
|
387841
|
+
}
|
|
387842
|
+
/** Arm the poller. Safe to call twice — the previous timers are replaced. */
|
|
387843
|
+
start() {
|
|
387844
|
+
this.arm();
|
|
387845
|
+
if (this.bootTimer !== null)
|
|
387846
|
+
clearTimeout(this.bootTimer);
|
|
387847
|
+
this.bootTimer = setTimeout(() => {
|
|
387848
|
+
this.bootTimer = null;
|
|
387849
|
+
void this.runSweep();
|
|
387850
|
+
}, this.initialDelayMs);
|
|
387851
|
+
this.bootTimer.unref?.();
|
|
387852
|
+
}
|
|
387853
|
+
/** Re-read the interval from settings and re-arm if it moved. */
|
|
387854
|
+
reschedule() {
|
|
387855
|
+
const next = clampUpdateCheckInterval(this.getIntervalSeconds());
|
|
387856
|
+
if (this.timer !== null && next === this.intervalSeconds)
|
|
387857
|
+
return;
|
|
387858
|
+
this.arm();
|
|
387859
|
+
}
|
|
387860
|
+
stop() {
|
|
387861
|
+
if (this.timer !== null)
|
|
387862
|
+
clearInterval(this.timer);
|
|
387863
|
+
if (this.bootTimer !== null)
|
|
387864
|
+
clearTimeout(this.bootTimer);
|
|
387865
|
+
this.timer = null;
|
|
387866
|
+
this.bootTimer = null;
|
|
387867
|
+
}
|
|
387868
|
+
/**
|
|
387869
|
+
* One full sweep. Never throws and never runs concurrently with itself — an
|
|
387870
|
+
* overlapping tick (slow registry, long agent fan-out) is dropped WITH a log
|
|
387871
|
+
* rather than doubling the npm traffic.
|
|
387872
|
+
*/
|
|
387873
|
+
async runSweep() {
|
|
387874
|
+
if (this.sweeping) {
|
|
387875
|
+
this.logger.warn("Update check skipped \u2014 previous sweep still running");
|
|
387876
|
+
return;
|
|
386616
387877
|
}
|
|
386617
|
-
|
|
386618
|
-
|
|
386619
|
-
|
|
387878
|
+
this.sweeping = true;
|
|
387879
|
+
try {
|
|
387880
|
+
const nodes = this.targets.listNodes();
|
|
387881
|
+
await this.attempt("hub addon packages", "hub", () => this.targets.checkHubAddons());
|
|
387882
|
+
await this.attempt("framework packages", "hub", () => this.targets.checkFrameworkPackages());
|
|
387883
|
+
for (const node of nodes) {
|
|
387884
|
+
if (!node.isOnline) {
|
|
387885
|
+
this.logger.warn("Update check skipped \u2014 node offline", { tags: { nodeId: node.id } });
|
|
387886
|
+
continue;
|
|
387887
|
+
}
|
|
387888
|
+
await this.attempt("node server package", node.id, () => this.targets.checkNodeServerUpdate(node.id, node.isHub));
|
|
387889
|
+
if (node.isHub)
|
|
387890
|
+
continue;
|
|
387891
|
+
await this.attempt("agent addon packages", node.id, () => this.targets.checkAgentAddons(node.id));
|
|
386620
387892
|
}
|
|
387893
|
+
} finally {
|
|
387894
|
+
this.sweeping = false;
|
|
386621
387895
|
}
|
|
386622
387896
|
}
|
|
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
|
|
387897
|
+
arm() {
|
|
387898
|
+
if (this.timer !== null)
|
|
387899
|
+
clearInterval(this.timer);
|
|
387900
|
+
this.intervalSeconds = clampUpdateCheckInterval(this.getIntervalSeconds());
|
|
387901
|
+
this.logger.info("Update check scheduled", {
|
|
387902
|
+
meta: { intervalSeconds: this.intervalSeconds }
|
|
386640
387903
|
});
|
|
387904
|
+
this.timer = setInterval(() => {
|
|
387905
|
+
void this.runSweep();
|
|
387906
|
+
}, this.intervalSeconds * 1e3);
|
|
387907
|
+
this.timer.unref?.();
|
|
386641
387908
|
}
|
|
386642
|
-
|
|
386643
|
-
|
|
387909
|
+
async attempt(what, nodeId, run) {
|
|
387910
|
+
try {
|
|
387911
|
+
await run();
|
|
387912
|
+
} catch (error) {
|
|
387913
|
+
this.logger.warn(`Update check failed \u2014 ${what}`, {
|
|
387914
|
+
tags: { nodeId },
|
|
387915
|
+
meta: { error: (0, types_1.errMsg)(error) }
|
|
387916
|
+
});
|
|
387917
|
+
}
|
|
386644
387918
|
}
|
|
386645
387919
|
};
|
|
386646
|
-
exports.
|
|
387920
|
+
exports.UpdateCheckScheduler = UpdateCheckScheduler;
|
|
386647
387921
|
}
|
|
386648
387922
|
});
|
|
386649
387923
|
|
|
@@ -386922,8 +388196,9 @@ var require_addon_package_service = __commonJS({
|
|
|
386922
388196
|
var node_util_1 = __require("util");
|
|
386923
388197
|
var system_1 = require_dist3();
|
|
386924
388198
|
var types_1 = require_dist9();
|
|
386925
|
-
var package_dir_utils_js_1 = require_package_dir_utils();
|
|
386926
388199
|
var update_availability_emitter_js_1 = require_update_availability_emitter();
|
|
388200
|
+
var update_check_scheduler_js_1 = require_update_check_scheduler();
|
|
388201
|
+
var package_dir_utils_js_1 = require_package_dir_utils();
|
|
386927
388202
|
var execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
386928
388203
|
exports.SYSTEM_PACKAGE = "@camstack/system";
|
|
386929
388204
|
exports.AUTO_UPDATE_EXCLUDED_PACKAGES = /* @__PURE__ */ new Set([
|
|
@@ -387016,10 +388291,20 @@ var require_addon_package_service = __commonJS({
|
|
|
387016
388291
|
updateAvailability;
|
|
387017
388292
|
// -- Auto-update state ----------------------------------------------------
|
|
387018
388293
|
autoUpdateConfig = {
|
|
387019
|
-
global: {
|
|
388294
|
+
global: {
|
|
388295
|
+
channel: "off",
|
|
388296
|
+
intervalSeconds: 21600,
|
|
388297
|
+
updateCheckIntervalSeconds: update_check_scheduler_js_1.DEFAULT_UPDATE_CHECK_INTERVAL_SECONDS
|
|
388298
|
+
},
|
|
387020
388299
|
overrides: {}
|
|
387021
388300
|
};
|
|
387022
388301
|
autoUpdateTimer = null;
|
|
388302
|
+
/**
|
|
388303
|
+
* Re-arm hook for the availability poller, wired at boot (manual-boot). Kept
|
|
388304
|
+
* as a callback rather than a service reference so this module never imports
|
|
388305
|
+
* the scheduler's dependencies back.
|
|
388306
|
+
*/
|
|
388307
|
+
updateCheckRescheduler = null;
|
|
387023
388308
|
// -- Timing constants -----------------------------------------------------
|
|
387024
388309
|
// Short TTL — operators expect to see freshly-published versions
|
|
387025
388310
|
// within minutes of `npm publish`, not hours. The Addons page kicks
|
|
@@ -387033,7 +388318,7 @@ var require_addon_package_service = __commonJS({
|
|
|
387033
388318
|
// 10 minutes
|
|
387034
388319
|
static NPM_REGISTRY = "https://registry.npmjs.org";
|
|
387035
388320
|
static REGISTRY_TIMEOUT_MS = 1e4;
|
|
387036
|
-
constructor(loggingService, eventBusService, configService, addonRegistry, notificationService, toastService) {
|
|
388321
|
+
constructor(loggingService, eventBusService, configService, addonRegistry, notificationService, toastService, updateAvailabilityStore) {
|
|
387037
388322
|
this.loggingService = loggingService;
|
|
387038
388323
|
this.eventBusService = eventBusService;
|
|
387039
388324
|
this.configService = configService;
|
|
@@ -387041,10 +388326,7 @@ var require_addon_package_service = __commonJS({
|
|
|
387041
388326
|
this.notificationService = notificationService;
|
|
387042
388327
|
this.toastService = toastService;
|
|
387043
388328
|
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
|
-
});
|
|
388329
|
+
this.updateAvailability = new update_availability_emitter_js_1.UpdateAvailabilityEmitter(this.eventBusService, { type: "core", id: "addon-package-service" }, updateAvailabilityStore);
|
|
387048
388330
|
try {
|
|
387049
388331
|
const addonsDir = this.resolveAddonsDir();
|
|
387050
388332
|
const workspacePackagesDir = (0, system_1.detectWorkspacePackagesDir)(__dirname);
|
|
@@ -387409,8 +388691,8 @@ var require_addon_package_service = __commonJS({
|
|
|
387409
388691
|
}
|
|
387410
388692
|
this.logger.info("Checking for package updates...");
|
|
387411
388693
|
const updates = [];
|
|
387412
|
-
const
|
|
387413
|
-
updates.push(...
|
|
388694
|
+
const sweep = await this.checkAddonPackageUpdates();
|
|
388695
|
+
updates.push(...sweep.updates);
|
|
387414
388696
|
this.logger.info("Found package updates", {
|
|
387415
388697
|
meta: {
|
|
387416
388698
|
count: updates.length,
|
|
@@ -387425,12 +388707,7 @@ var require_addon_package_service = __commonJS({
|
|
|
387425
388707
|
updates,
|
|
387426
388708
|
expiresAt: now + _AddonPackageService.UPDATE_CACHE_TTL_MS
|
|
387427
388709
|
};
|
|
387428
|
-
this.
|
|
387429
|
-
target: "addon",
|
|
387430
|
-
packageName: update.name,
|
|
387431
|
-
currentVersion: update.currentVersion,
|
|
387432
|
-
latestVersion: update.latestVersion
|
|
387433
|
-
})));
|
|
388710
|
+
this.publishAddonAvailability(updates, sweep.failures);
|
|
387434
388711
|
return updates;
|
|
387435
388712
|
}
|
|
387436
388713
|
/** Clear the cached update check results */
|
|
@@ -387454,12 +388731,22 @@ var require_addon_package_service = __commonJS({
|
|
|
387454
388731
|
seen.set(pkg.name, pkg.version);
|
|
387455
388732
|
}
|
|
387456
388733
|
const updates = [];
|
|
388734
|
+
let failures = 0;
|
|
387457
388735
|
await Promise.all([...seen].map(async ([name, version]) => {
|
|
387458
388736
|
if (!this.isAllowedPackage(name))
|
|
387459
388737
|
return;
|
|
387460
388738
|
if (exports.AUTO_UPDATE_EXCLUDED_PACKAGES.has(name))
|
|
387461
388739
|
return;
|
|
387462
|
-
const
|
|
388740
|
+
const lookup = await this.lookupLatestVersion(name);
|
|
388741
|
+
if (!lookup.ok) {
|
|
388742
|
+
failures += 1;
|
|
388743
|
+
this.logger.warn("Registry lookup failed for installed package", {
|
|
388744
|
+
tags: { nodeId: nodeId ?? "hub" },
|
|
388745
|
+
meta: { name, error: lookup.error }
|
|
388746
|
+
});
|
|
388747
|
+
return;
|
|
388748
|
+
}
|
|
388749
|
+
const latestVersion = lookup.latestVersion;
|
|
387463
388750
|
if (latestVersion === null || !isVersionNewer(latestVersion, version))
|
|
387464
388751
|
return;
|
|
387465
388752
|
const category = this.categorize(name);
|
|
@@ -387471,13 +388758,7 @@ var require_addon_package_service = __commonJS({
|
|
|
387471
388758
|
requiresRestart: category === "core"
|
|
387472
388759
|
});
|
|
387473
388760
|
}));
|
|
387474
|
-
this.
|
|
387475
|
-
target: "addon",
|
|
387476
|
-
packageName: update.name,
|
|
387477
|
-
currentVersion: update.currentVersion,
|
|
387478
|
-
latestVersion: update.latestVersion,
|
|
387479
|
-
...nodeId !== void 0 ? { nodeId } : {}
|
|
387480
|
-
})), nodeId);
|
|
388761
|
+
this.publishAddonAvailability(updates, failures, nodeId);
|
|
387481
388762
|
return updates;
|
|
387482
388763
|
}
|
|
387483
388764
|
/**
|
|
@@ -387854,10 +389135,15 @@ var require_addon_package_service = __commonJS({
|
|
|
387854
389135
|
*/
|
|
387855
389136
|
restartServer(requestedBy) {
|
|
387856
389137
|
this.logger.info("Server restart requested -- initiating graceful shutdown");
|
|
389138
|
+
const toVersion = toVersionFromRestartReason(requestedBy);
|
|
387857
389139
|
const payload = {
|
|
387858
|
-
kind: "manual",
|
|
389140
|
+
kind: isServerUpdateRequest(requestedBy) ? "framework-update" : "manual",
|
|
387859
389141
|
requestedAt: Date.now(),
|
|
387860
|
-
...requestedBy !== void 0 ? { requestedBy } : {}
|
|
389142
|
+
...requestedBy !== void 0 ? { requestedBy } : {},
|
|
389143
|
+
...isServerUpdateRequest(requestedBy) ? {
|
|
389144
|
+
packageName: "@camstack/server",
|
|
389145
|
+
...toVersion !== void 0 ? { toVersion } : {}
|
|
389146
|
+
} : {}
|
|
387861
389147
|
};
|
|
387862
389148
|
try {
|
|
387863
389149
|
(0, system_1.writePendingRestart)(this.resolveDataDir(), payload);
|
|
@@ -387899,6 +389185,7 @@ var require_addon_package_service = __commonJS({
|
|
|
387899
389185
|
const hubPkgDir = (0, package_dir_utils_js_1.resolveHubClosurePackageDir)(packageName);
|
|
387900
389186
|
const buildId = hubPkgDir !== null ? (0, package_dir_utils_js_1.computeDistBuildId)(path.join(hubPkgDir, "dist")) : null;
|
|
387901
389187
|
let latestVersion = null;
|
|
389188
|
+
let lookupFailed = false;
|
|
387902
389189
|
try {
|
|
387903
389190
|
const args = [
|
|
387904
389191
|
"view",
|
|
@@ -387910,21 +389197,65 @@ var require_addon_package_service = __commonJS({
|
|
|
387910
389197
|
const trimmed = stdout.trim();
|
|
387911
389198
|
latestVersion = trimmed.length > 0 ? trimmed : null;
|
|
387912
389199
|
} catch (err) {
|
|
389200
|
+
lookupFailed = true;
|
|
387913
389201
|
this.logger.debug("listFrameworkPackages: npm view failed", {
|
|
387914
389202
|
meta: { packageName, error: (0, types_1.errMsg)(err) }
|
|
387915
389203
|
});
|
|
387916
389204
|
}
|
|
387917
|
-
const hasUpdate = latestVersion !== null && currentVersion !== "unknown" && latestVersion
|
|
389205
|
+
const hasUpdate = latestVersion !== null && currentVersion !== "unknown" && isVersionNewer(latestVersion, currentVersion);
|
|
387918
389206
|
return {
|
|
387919
389207
|
packageName,
|
|
387920
389208
|
currentVersion,
|
|
387921
389209
|
latestVersion,
|
|
387922
389210
|
hasUpdate,
|
|
387923
389211
|
buildId,
|
|
389212
|
+
lookupFailed,
|
|
387924
389213
|
...description !== void 0 ? { description } : {}
|
|
387925
389214
|
};
|
|
387926
389215
|
}));
|
|
387927
|
-
|
|
389216
|
+
this.publishFrameworkAvailability(rows);
|
|
389217
|
+
return rows.map(({ lookupFailed: _lookupFailed, ...row }) => row);
|
|
389218
|
+
}
|
|
389219
|
+
/**
|
|
389220
|
+
* Announce framework updates. This surface SHOWED `@camstack/system` had an
|
|
389221
|
+
* update and emitted nothing, so the operator was never told — it was the
|
|
389222
|
+
* only discovery path with no publish at all.
|
|
389223
|
+
*
|
|
389224
|
+
* Two rules it must not break:
|
|
389225
|
+
* - **Strictly newer only.** `hasUpdate` is `latest !== current`, which is
|
|
389226
|
+
* true for a hub running a `-dev.<timestamp>` build AHEAD of the npm tag.
|
|
389227
|
+
* Announcing that would be telling the operator to downgrade.
|
|
389228
|
+
* - **A failed lookup publishes nothing.** Not even an empty snapshot: an
|
|
389229
|
+
* empty snapshot means "up to date" and would clear the dedup state, so
|
|
389230
|
+
* the next successful poll would re-announce everything.
|
|
389231
|
+
*/
|
|
389232
|
+
publishFrameworkAvailability(rows) {
|
|
389233
|
+
const failed = rows.filter((row) => row.lookupFailed);
|
|
389234
|
+
if (failed.length > 0) {
|
|
389235
|
+
this.logger.warn("Framework update check incomplete \u2014 availability not published", {
|
|
389236
|
+
meta: { packages: failed.map((row) => row.packageName) }
|
|
389237
|
+
});
|
|
389238
|
+
return;
|
|
389239
|
+
}
|
|
389240
|
+
const candidates = [];
|
|
389241
|
+
for (const row of rows) {
|
|
389242
|
+
if (row.latestVersion === null || row.currentVersion === "unknown")
|
|
389243
|
+
continue;
|
|
389244
|
+
if (!isVersionNewer(row.latestVersion, row.currentVersion))
|
|
389245
|
+
continue;
|
|
389246
|
+
candidates.push({
|
|
389247
|
+
target: "server",
|
|
389248
|
+
packageName: row.packageName,
|
|
389249
|
+
currentVersion: row.currentVersion,
|
|
389250
|
+
latestVersion: row.latestVersion,
|
|
389251
|
+
nodeId: this.resolveNodeId()
|
|
389252
|
+
});
|
|
389253
|
+
}
|
|
389254
|
+
this.updateAvailability.publishSnapshot("server", candidates, this.resolveNodeId());
|
|
389255
|
+
}
|
|
389256
|
+
/** Node id stamped on availability events emitted by THIS process. */
|
|
389257
|
+
resolveNodeId() {
|
|
389258
|
+
return process.env["CAMSTACK_NODE_ID"] ?? "hub";
|
|
387928
389259
|
}
|
|
387929
389260
|
// =========================================================================
|
|
387930
389261
|
// Reload
|
|
@@ -387956,21 +389287,37 @@ var require_addon_package_service = __commonJS({
|
|
|
387956
389287
|
// =========================================================================
|
|
387957
389288
|
// Auto-update settings
|
|
387958
389289
|
// =========================================================================
|
|
389290
|
+
/**
|
|
389291
|
+
* Register the availability poller's re-arm hook. Called once at boot so a
|
|
389292
|
+
* `setAutoUpdateSettings` write takes effect without a restart.
|
|
389293
|
+
*/
|
|
389294
|
+
setUpdateCheckRescheduler(reschedule) {
|
|
389295
|
+
this.updateCheckRescheduler = reschedule;
|
|
389296
|
+
}
|
|
387959
389297
|
/** Get global auto-update settings */
|
|
387960
389298
|
getAutoUpdateSettings() {
|
|
387961
389299
|
return { ...this.autoUpdateConfig.global };
|
|
387962
389300
|
}
|
|
387963
389301
|
/** Set global auto-update settings and restart the timer */
|
|
387964
|
-
async setAutoUpdateSettings(channel, intervalSeconds) {
|
|
389302
|
+
async setAutoUpdateSettings(channel, intervalSeconds, updateCheckIntervalSeconds) {
|
|
389303
|
+
const previousCheckInterval = this.autoUpdateConfig.global.updateCheckIntervalSeconds;
|
|
389304
|
+
const nextCheckInterval = updateCheckIntervalSeconds === void 0 ? previousCheckInterval : (0, update_check_scheduler_js_1.clampUpdateCheckInterval)(updateCheckIntervalSeconds);
|
|
387965
389305
|
this.autoUpdateConfig = {
|
|
387966
389306
|
...this.autoUpdateConfig,
|
|
387967
389307
|
global: {
|
|
387968
389308
|
channel,
|
|
387969
|
-
intervalSeconds: intervalSeconds ?? this.autoUpdateConfig.global.intervalSeconds
|
|
389309
|
+
intervalSeconds: intervalSeconds ?? this.autoUpdateConfig.global.intervalSeconds,
|
|
389310
|
+
updateCheckIntervalSeconds: nextCheckInterval
|
|
387970
389311
|
}
|
|
387971
389312
|
};
|
|
387972
389313
|
this.saveAutoUpdateConfig();
|
|
387973
389314
|
this.scheduleAutoUpdate();
|
|
389315
|
+
if (updateCheckIntervalSeconds === void 0)
|
|
389316
|
+
return;
|
|
389317
|
+
this.logger.info("Update-check interval changed", {
|
|
389318
|
+
meta: { from: previousCheckInterval, to: nextCheckInterval }
|
|
389319
|
+
});
|
|
389320
|
+
this.updateCheckRescheduler?.();
|
|
387974
389321
|
}
|
|
387975
389322
|
/** Get per-addon auto-update override */
|
|
387976
389323
|
getAddonAutoUpdate(addonId) {
|
|
@@ -388101,10 +389448,12 @@ var require_addon_package_service = __commonJS({
|
|
|
388101
389448
|
const global2 = asRecord(raw["global"]);
|
|
388102
389449
|
const channel = asString(global2["channel"]);
|
|
388103
389450
|
const validChannel = channel === "latest" || channel === "beta" ? channel : "off";
|
|
389451
|
+
const rawCheckInterval = global2["updateCheckIntervalSeconds"];
|
|
388104
389452
|
return {
|
|
388105
389453
|
global: {
|
|
388106
389454
|
channel: validChannel,
|
|
388107
|
-
intervalSeconds: typeof global2["intervalSeconds"] === "number" ? global2["intervalSeconds"] : 3600
|
|
389455
|
+
intervalSeconds: typeof global2["intervalSeconds"] === "number" ? global2["intervalSeconds"] : 3600,
|
|
389456
|
+
updateCheckIntervalSeconds: typeof rawCheckInterval === "number" ? (0, update_check_scheduler_js_1.clampUpdateCheckInterval)(rawCheckInterval) : update_check_scheduler_js_1.DEFAULT_UPDATE_CHECK_INTERVAL_SECONDS
|
|
388108
389457
|
},
|
|
388109
389458
|
overrides: Object.fromEntries(Object.entries(asRecord(raw["overrides"])).map(([k, v]) => {
|
|
388110
389459
|
const s = asString(v);
|
|
@@ -388119,7 +389468,14 @@ var require_addon_package_service = __commonJS({
|
|
|
388119
389468
|
meta: { error: (0, types_1.errMsg)(err) }
|
|
388120
389469
|
});
|
|
388121
389470
|
}
|
|
388122
|
-
return {
|
|
389471
|
+
return {
|
|
389472
|
+
global: {
|
|
389473
|
+
channel: "off",
|
|
389474
|
+
intervalSeconds: 21600,
|
|
389475
|
+
updateCheckIntervalSeconds: update_check_scheduler_js_1.DEFAULT_UPDATE_CHECK_INTERVAL_SECONDS
|
|
389476
|
+
},
|
|
389477
|
+
overrides: {}
|
|
389478
|
+
};
|
|
388123
389479
|
}
|
|
388124
389480
|
/** Save auto-update config to disk */
|
|
388125
389481
|
saveAutoUpdateConfig() {
|
|
@@ -388144,12 +389500,17 @@ var require_addon_package_service = __commonJS({
|
|
|
388144
389500
|
/**
|
|
388145
389501
|
* Check addon packages for updates by reading installed versions from
|
|
388146
389502
|
* data/addons/{name}/package.json and comparing against npm registry.
|
|
389503
|
+
*
|
|
389504
|
+
* Reports `failures` alongside the diff: a registry lookup that could not be
|
|
389505
|
+
* MADE is not evidence that a package is up to date, and the availability
|
|
389506
|
+
* publish branches on it.
|
|
388147
389507
|
*/
|
|
388148
389508
|
async checkAddonPackageUpdates() {
|
|
388149
389509
|
const addonsDir = this.resolveAddonsDir();
|
|
388150
389510
|
const updates = [];
|
|
389511
|
+
let failures = 0;
|
|
388151
389512
|
if (!fs.existsSync(addonsDir))
|
|
388152
|
-
return updates;
|
|
389513
|
+
return { updates, failures };
|
|
388153
389514
|
const pkgJsonPaths = [];
|
|
388154
389515
|
const topDirs = fs.readdirSync(addonsDir, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
388155
389516
|
for (const dir of topDirs) {
|
|
@@ -388186,7 +389547,16 @@ var require_addon_package_service = __commonJS({
|
|
|
388186
389547
|
if (source === "workspace")
|
|
388187
389548
|
continue;
|
|
388188
389549
|
}
|
|
388189
|
-
const
|
|
389550
|
+
const lookup = await this.lookupLatestVersion(name);
|
|
389551
|
+
if (!lookup.ok) {
|
|
389552
|
+
failures += 1;
|
|
389553
|
+
this.logger.warn("Registry lookup failed for installed addon package", {
|
|
389554
|
+
tags: { nodeId: this.resolveNodeId() },
|
|
389555
|
+
meta: { name, error: lookup.error }
|
|
389556
|
+
});
|
|
389557
|
+
continue;
|
|
389558
|
+
}
|
|
389559
|
+
const latestVersion = lookup.latestVersion;
|
|
388190
389560
|
if (!latestVersion)
|
|
388191
389561
|
continue;
|
|
388192
389562
|
if (isVersionNewer(latestVersion, version)) {
|
|
@@ -388199,13 +389569,14 @@ var require_addon_package_service = __commonJS({
|
|
|
388199
389569
|
});
|
|
388200
389570
|
}
|
|
388201
389571
|
} catch (error) {
|
|
389572
|
+
failures += 1;
|
|
388202
389573
|
const msg = (0, types_1.errMsg)(error);
|
|
388203
|
-
this.logger.
|
|
389574
|
+
this.logger.warn("Failed to check updates for addon", {
|
|
388204
389575
|
meta: { pkgJsonPath, error: msg }
|
|
388205
389576
|
});
|
|
388206
389577
|
}
|
|
388207
389578
|
}
|
|
388208
|
-
return updates;
|
|
389579
|
+
return { updates, failures };
|
|
388209
389580
|
}
|
|
388210
389581
|
// =========================================================================
|
|
388211
389582
|
// Private: npm registry helpers
|
|
@@ -388216,7 +389587,7 @@ var require_addon_package_service = __commonJS({
|
|
|
388216
389587
|
* Honours `CAMSTACK_NPM_REGISTRY` so update checks resolve against
|
|
388217
389588
|
* the same registry the installer/pack paths use. Without this, a
|
|
388218
389589
|
* per-node `listUpdates` (which diffs an agent's roster via
|
|
388219
|
-
* `checkUpdatesForInstalled` → `
|
|
389590
|
+
* `checkUpdatesForInstalled` → `lookupLatestVersion`) would bypass a
|
|
388220
389591
|
* private registry — including the e2e harness's verdaccio — and
|
|
388221
389592
|
* silently report "no update" for packages that only exist there.
|
|
388222
389593
|
* Trailing slashes are stripped so the `${base}/${name}` join is clean.
|
|
@@ -388226,28 +389597,60 @@ var require_addon_package_service = __commonJS({
|
|
|
388226
389597
|
const base = override && override.length > 0 ? override : _AddonPackageService.NPM_REGISTRY;
|
|
388227
389598
|
return base.replace(/\/+$/, "");
|
|
388228
389599
|
}
|
|
388229
|
-
/**
|
|
388230
|
-
|
|
389600
|
+
/**
|
|
389601
|
+
* Ask the registry for a package's latest version, distinguishing "there is
|
|
389602
|
+
* no such package" (a definitive answer) from "I could not ask" (a failure).
|
|
389603
|
+
* The difference decides whether an availability SNAPSHOT may be published —
|
|
389604
|
+
* see {@link RegistryLookup}.
|
|
389605
|
+
*/
|
|
389606
|
+
async lookupLatestVersion(packageName) {
|
|
388231
389607
|
try {
|
|
388232
389608
|
const encodedName = packageName.replace("/", "%2F");
|
|
388233
389609
|
const url = `${this.resolveRegistryBase()}/${encodedName}/latest`;
|
|
388234
389610
|
const response = await fetch(url, {
|
|
388235
389611
|
signal: AbortSignal.timeout(_AddonPackageService.REGISTRY_TIMEOUT_MS)
|
|
388236
389612
|
});
|
|
389613
|
+
if (response.status === 404)
|
|
389614
|
+
return { ok: true, latestVersion: null };
|
|
388237
389615
|
if (!response.ok) {
|
|
388238
389616
|
this.logger.debug("Registry returned non-ok status", {
|
|
388239
389617
|
meta: { packageName, status: response.status }
|
|
388240
389618
|
});
|
|
388241
|
-
return
|
|
389619
|
+
return { ok: false, error: `registry status ${response.status}` };
|
|
388242
389620
|
}
|
|
388243
389621
|
const data = await fetchJsonObject(response);
|
|
388244
389622
|
const version = asString(data["version"]);
|
|
388245
|
-
return version || null;
|
|
389623
|
+
return { ok: true, latestVersion: version || null };
|
|
388246
389624
|
} catch (error) {
|
|
388247
|
-
|
|
388248
|
-
|
|
388249
|
-
|
|
389625
|
+
return { ok: false, error: (0, types_1.errMsg)(error) };
|
|
389626
|
+
}
|
|
389627
|
+
}
|
|
389628
|
+
/**
|
|
389629
|
+
* Announce the addon-update set for one scope (hub or a node).
|
|
389630
|
+
*
|
|
389631
|
+
* `failures > 0` means at least one registry lookup could not be made, so
|
|
389632
|
+
* the set is INCOMPLETE — publish it as candidates (additive) rather than a
|
|
389633
|
+
* snapshot (authoritative). A snapshot built from a partial sweep silently
|
|
389634
|
+
* marks the unreachable packages "up to date", clears their dedup state and
|
|
389635
|
+
* re-announces them on the next good poll.
|
|
389636
|
+
*/
|
|
389637
|
+
publishAddonAvailability(updates, failures, nodeId) {
|
|
389638
|
+
const candidates = updates.map((update) => ({
|
|
389639
|
+
target: "addon",
|
|
389640
|
+
packageName: update.name,
|
|
389641
|
+
currentVersion: update.currentVersion,
|
|
389642
|
+
latestVersion: update.latestVersion,
|
|
389643
|
+
...nodeId !== void 0 ? { nodeId } : {}
|
|
389644
|
+
}));
|
|
389645
|
+
if (failures > 0) {
|
|
389646
|
+
this.logger.warn("Addon update check incomplete \u2014 availability published as partial", {
|
|
389647
|
+
tags: { nodeId: nodeId ?? "hub" },
|
|
389648
|
+
meta: { failures, published: candidates.length }
|
|
389649
|
+
});
|
|
389650
|
+
this.updateAvailability.publishCandidates(candidates);
|
|
389651
|
+
return;
|
|
388250
389652
|
}
|
|
389653
|
+
this.updateAvailability.publishSnapshot("addon", candidates, nodeId);
|
|
388251
389654
|
}
|
|
388252
389655
|
/** Fetch npm search results for camstack addon packages (cached 5 min) */
|
|
388253
389656
|
async fetchSearchFromNpm() {
|
|
@@ -388397,6 +389800,15 @@ var require_addon_package_service = __commonJS({
|
|
|
388397
389800
|
}
|
|
388398
389801
|
};
|
|
388399
389802
|
exports.AddonPackageService = AddonPackageService;
|
|
389803
|
+
function isServerUpdateRequest(requestedBy) {
|
|
389804
|
+
return requestedBy !== void 0 && requestedBy.startsWith("server-update:");
|
|
389805
|
+
}
|
|
389806
|
+
function toVersionFromRestartReason(requestedBy) {
|
|
389807
|
+
if (requestedBy === void 0)
|
|
389808
|
+
return void 0;
|
|
389809
|
+
const match = /@([^@\s]+)$/.exec(requestedBy);
|
|
389810
|
+
return match?.[1];
|
|
389811
|
+
}
|
|
388400
389812
|
async function extractTgzStripped(tgz, destDir) {
|
|
388401
389813
|
fs.mkdirSync(destDir, { recursive: true });
|
|
388402
389814
|
const tmpFile = path.join(os.tmpdir(), `camstack-tgz-${(0, node_crypto_1.randomUUID)()}.tgz`);
|
|
@@ -396513,6 +397925,30 @@ var require_integration_id_backfill = __commonJS({
|
|
|
396513
397925
|
}
|
|
396514
397926
|
});
|
|
396515
397927
|
|
|
397928
|
+
// ../../server/backend/dist/core/updates/agent-installed-packages.js
|
|
397929
|
+
var require_agent_installed_packages = __commonJS({
|
|
397930
|
+
"../../server/backend/dist/core/updates/agent-installed-packages.js"(exports) {
|
|
397931
|
+
"use strict";
|
|
397932
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
397933
|
+
exports.AGENT_STATUS_TIMEOUT_MS = void 0;
|
|
397934
|
+
exports.fetchAgentInstalledPackages = fetchAgentInstalledPackages;
|
|
397935
|
+
var addon_package_service_js_1 = require_addon_package_service();
|
|
397936
|
+
exports.AGENT_STATUS_TIMEOUT_MS = 5e3;
|
|
397937
|
+
async function fetchAgentInstalledPackages(broker, nodeId) {
|
|
397938
|
+
const status = await broker.call("$agent.status", {}, { nodeID: nodeId, timeout: exports.AGENT_STATUS_TIMEOUT_MS });
|
|
397939
|
+
const out = [];
|
|
397940
|
+
for (const addon of status.addons ?? []) {
|
|
397941
|
+
if (typeof addon.packageName !== "string" || typeof addon.version !== "string")
|
|
397942
|
+
continue;
|
|
397943
|
+
if ((0, addon_package_service_js_1.isFrameworkPackage)(addon.packageName))
|
|
397944
|
+
continue;
|
|
397945
|
+
out.push({ name: addon.packageName, version: addon.version });
|
|
397946
|
+
}
|
|
397947
|
+
return out;
|
|
397948
|
+
}
|
|
397949
|
+
}
|
|
397950
|
+
});
|
|
397951
|
+
|
|
396516
397952
|
// ../../server/backend/dist/api/core/collection-preference.js
|
|
396517
397953
|
var require_collection_preference = __commonJS({
|
|
396518
397954
|
"../../server/backend/dist/api/core/collection-preference.js"(exports) {
|
|
@@ -396888,6 +398324,7 @@ var require_post_boot_service = __commonJS({
|
|
|
396888
398324
|
const payload = {
|
|
396889
398325
|
kind: marker.kind,
|
|
396890
398326
|
requestedAt: marker.requestedAt,
|
|
398327
|
+
nodeId: process.env["CAMSTACK_NODE_ID"] ?? "hub",
|
|
396891
398328
|
...marker.packageName !== void 0 ? { packageName: marker.packageName } : {},
|
|
396892
398329
|
...marker.fromVersion !== void 0 ? { fromVersion: marker.fromVersion } : {},
|
|
396893
398330
|
...marker.toVersion !== void 0 ? { toVersion: marker.toVersion } : {},
|
|
@@ -396971,6 +398408,7 @@ var require_cap_providers = __commonJS({
|
|
|
396971
398408
|
var integration_id_backfill_1 = require_integration_id_backfill();
|
|
396972
398409
|
var addon_package_service_js_1 = require_addon_package_service();
|
|
396973
398410
|
var lifecycle_runner_singleton_js_1 = require_lifecycle_runner_singleton();
|
|
398411
|
+
var agent_installed_packages_js_1 = require_agent_installed_packages();
|
|
396974
398412
|
var collection_preference_js_1 = require_collection_preference();
|
|
396975
398413
|
var site_location_js_1 = require_site_location();
|
|
396976
398414
|
function getRetention(registry) {
|
|
@@ -397854,18 +399292,6 @@ var require_cap_providers = __commonJS({
|
|
|
397854
399292
|
function isHubNode(nodeId) {
|
|
397855
399293
|
return nodeId === "hub" || nodeId.startsWith("hub/");
|
|
397856
399294
|
}
|
|
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
399295
|
function buildAddonsProvider(ar, ps, ls, moleculer, configService, ctx) {
|
|
397870
399296
|
const broker = moleculer.broker;
|
|
397871
399297
|
const frameworkAllowSet = /* @__PURE__ */ new Set([addon_package_service_js_1.SYSTEM_PACKAGE]);
|
|
@@ -397906,7 +399332,7 @@ var require_cap_providers = __commonJS({
|
|
|
397906
399332
|
},
|
|
397907
399333
|
listUpdates: async (input) => {
|
|
397908
399334
|
const nodeId = input.nodeId;
|
|
397909
|
-
const updates = nodeId === void 0 || isHubNode(nodeId) ? await ps.checkUpdates() : await ps.checkUpdatesForInstalled(await fetchAgentInstalledPackages(broker, nodeId), nodeId);
|
|
399335
|
+
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
399336
|
return updates.map((u) => ({ ...u, isSystem: frameworkAllowSet.has(u.name) }));
|
|
397911
399337
|
},
|
|
397912
399338
|
updatePackage: async (input) => {
|
|
@@ -397935,7 +399361,7 @@ var require_cap_providers = __commonJS({
|
|
|
397935
399361
|
const nodeId = input.nodeId;
|
|
397936
399362
|
if (nodeId === void 0 || isHubNode(nodeId))
|
|
397937
399363
|
return ps.checkUpdates(true);
|
|
397938
|
-
const installed = await fetchAgentInstalledPackages(broker, nodeId);
|
|
399364
|
+
const installed = await (0, agent_installed_packages_js_1.fetchAgentInstalledPackages)(broker, nodeId);
|
|
397939
399365
|
return ps.checkUpdatesForInstalled(installed, nodeId);
|
|
397940
399366
|
},
|
|
397941
399367
|
restartServer: async () => ps.restartServer(ctx.user?.username ?? ctx.user?.id),
|
|
@@ -397996,7 +399422,7 @@ var require_cap_providers = __commonJS({
|
|
|
397996
399422
|
return { success: true };
|
|
397997
399423
|
},
|
|
397998
399424
|
getAutoUpdateSettings: async () => ps.getAutoUpdateSettings(),
|
|
397999
|
-
setAutoUpdateSettings: async (input) => ps.setAutoUpdateSettings(input.channel, input.intervalSeconds),
|
|
399425
|
+
setAutoUpdateSettings: async (input) => ps.setAutoUpdateSettings(input.channel, input.intervalSeconds, input.updateCheckIntervalSeconds),
|
|
398000
399426
|
getAddonAutoUpdate: async (input) => ps.getAddonAutoUpdate(input.addonId),
|
|
398001
399427
|
setAddonAutoUpdate: async (input) => ps.setAddonAutoUpdate(input.addonId, input.channel),
|
|
398002
399428
|
applyAutoUpdateToAll: async (input) => {
|
|
@@ -398243,6 +399669,12 @@ var require_event_bus_proxy_router = __commonJS({
|
|
|
398243
399669
|
}
|
|
398244
399670
|
function createEventBusProxyRouter(eventBus) {
|
|
398245
399671
|
return (0, trpc_middleware_js_1.trpcRouter)({
|
|
399672
|
+
// ADMIN-ONLY. `emit` injects a fully-attributed SystemEvent onto the hub's
|
|
399673
|
+
// real EventBus — including a spoofable `source` (a fake `device`/`addon`
|
|
399674
|
+
// origin). Forked addons that legitimately emit reach this over the
|
|
399675
|
+
// trusted core-cap/UDS mesh, whose context is `createMeshTrpcContext`
|
|
399676
|
+
// (`isAdmin: true`), so gating it here withholds the surface from
|
|
399677
|
+
// non-admin HTTP/WS principals without blinding a worker.
|
|
398246
399678
|
emit: trpc_middleware_js_1.adminProcedure.input(SystemEventInputSchema).output(zod_1.z.object({ ok: zod_1.z.literal(true) })).mutation(({ input }) => {
|
|
398247
399679
|
eventBus.emit({
|
|
398248
399680
|
id: input.id,
|
|
@@ -404158,7 +405590,7 @@ var require_logging_service = __commonJS({
|
|
|
404158
405590
|
deviceNames = /* @__PURE__ */ new Map();
|
|
404159
405591
|
constructor(configService) {
|
|
404160
405592
|
const perAddonCapacity = configService.get("eventBus.perAddonLogBufferSize") ?? 5e3;
|
|
404161
|
-
const maxTotalEntries = configService.get("eventBus.maxTotalLogBufferSize")
|
|
405593
|
+
const maxTotalEntries = configService.get("eventBus.maxTotalLogBufferSize");
|
|
404162
405594
|
const pruneLevel = configService.get("eventBus.logBufferPruneLevel") ?? "debug";
|
|
404163
405595
|
super(perAddonCapacity, { maxTotalEntries, pruneLevel });
|
|
404164
405596
|
this.setDeviceNameLookup((id) => this.deviceNames.get(id) ?? null);
|
|
@@ -405500,11 +406932,12 @@ var require_server_update_service = __commonJS({
|
|
|
405500
406932
|
exports.ServerUpdateService = void 0;
|
|
405501
406933
|
var path = __importStar(__require("path"));
|
|
405502
406934
|
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
406935
|
var update_availability_emitter_js_1 = require_update_availability_emitter();
|
|
406936
|
+
var system_ensure_prebuilds_js_1 = require_system_ensure_prebuilds();
|
|
406937
|
+
var system_exec_npm_js_1 = require_system_exec_npm();
|
|
405506
406938
|
var ServerUpdateService = class extends index_js_1.RootUpdateService {
|
|
405507
406939
|
updateAvailability;
|
|
406940
|
+
nodeId;
|
|
405508
406941
|
constructor(options) {
|
|
405509
406942
|
const dataDir = path.resolve(options.dataDir ?? options.env?.["CAMSTACK_DATA"] ?? process.env["CAMSTACK_DATA"] ?? "camstack-data");
|
|
405510
406943
|
super({
|
|
@@ -405535,23 +406968,23 @@ var require_server_update_service = __commonJS({
|
|
|
405535
406968
|
env: options.env,
|
|
405536
406969
|
now: options.now
|
|
405537
406970
|
});
|
|
405538
|
-
this.updateAvailability = options.eventBus !== void 0 ? new update_availability_emitter_js_1.UpdateAvailabilityEmitter(options.eventBus, {
|
|
405539
|
-
|
|
405540
|
-
id: "server-update-service"
|
|
405541
|
-
}) : null;
|
|
406971
|
+
this.updateAvailability = options.eventBus !== void 0 ? new update_availability_emitter_js_1.UpdateAvailabilityEmitter(options.eventBus, { type: "core", id: "server-update-service" }, options.updateAvailabilityStore) : null;
|
|
406972
|
+
this.nodeId = options.nodeId ?? "hub";
|
|
405542
406973
|
}
|
|
405543
406974
|
async checkServerUpdate() {
|
|
405544
406975
|
const result = await super.checkServerUpdate();
|
|
405545
406976
|
if (result.error !== null)
|
|
405546
406977
|
return result;
|
|
406978
|
+
const nodeId = this.nodeId;
|
|
405547
406979
|
this.updateAvailability?.publishSnapshot("server", result.updateAvailable && result.runningVersion !== null && result.latestVersion !== null ? [
|
|
405548
406980
|
{
|
|
405549
406981
|
target: "server",
|
|
405550
406982
|
packageName: result.packageName,
|
|
405551
406983
|
currentVersion: result.runningVersion,
|
|
405552
|
-
latestVersion: result.latestVersion
|
|
406984
|
+
latestVersion: result.latestVersion,
|
|
406985
|
+
...nodeId !== void 0 ? { nodeId } : {}
|
|
405553
406986
|
}
|
|
405554
|
-
] : []);
|
|
406987
|
+
] : [], nodeId);
|
|
405555
406988
|
return result;
|
|
405556
406989
|
}
|
|
405557
406990
|
};
|
|
@@ -405572,6 +407005,82 @@ var require_storage_service = __commonJS({
|
|
|
405572
407005
|
}
|
|
405573
407006
|
});
|
|
405574
407007
|
|
|
407008
|
+
// ../../server/backend/dist/core/streaming/ttl-cache.js
|
|
407009
|
+
var require_ttl_cache = __commonJS({
|
|
407010
|
+
"../../server/backend/dist/core/streaming/ttl-cache.js"(exports) {
|
|
407011
|
+
"use strict";
|
|
407012
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
407013
|
+
exports.TtlCache = void 0;
|
|
407014
|
+
var TtlCache = class {
|
|
407015
|
+
entries = /* @__PURE__ */ new Map();
|
|
407016
|
+
ttlMs;
|
|
407017
|
+
maxEntries;
|
|
407018
|
+
now;
|
|
407019
|
+
constructor(options) {
|
|
407020
|
+
this.ttlMs = options.ttlMs;
|
|
407021
|
+
this.maxEntries = options.maxEntries;
|
|
407022
|
+
this.now = options.now ?? (() => Date.now());
|
|
407023
|
+
}
|
|
407024
|
+
/** The live value, or undefined when absent or expired. An expired entry is
|
|
407025
|
+
* DELETED here, not merely reported missing. */
|
|
407026
|
+
get(key) {
|
|
407027
|
+
const slot = this.entries.get(key);
|
|
407028
|
+
if (slot === void 0)
|
|
407029
|
+
return void 0;
|
|
407030
|
+
const at = this.now();
|
|
407031
|
+
if (at - slot.storedAt >= this.ttlMs) {
|
|
407032
|
+
this.entries.delete(key);
|
|
407033
|
+
return void 0;
|
|
407034
|
+
}
|
|
407035
|
+
slot.lastAccessAt = at;
|
|
407036
|
+
return slot.value;
|
|
407037
|
+
}
|
|
407038
|
+
/**
|
|
407039
|
+
* Store a value, then bring the map back inside both bounds.
|
|
407040
|
+
*
|
|
407041
|
+
* The sweep runs on WRITE and not on a timer on purpose: a timer would have
|
|
407042
|
+
* to be owned, unref'd and stopped by every holder of a cache, and a cache
|
|
407043
|
+
* that is never written to is a cache that is not growing.
|
|
407044
|
+
*/
|
|
407045
|
+
set(key, value) {
|
|
407046
|
+
const at = this.now();
|
|
407047
|
+
this.entries.set(key, { value, storedAt: at, lastAccessAt: at });
|
|
407048
|
+
this.sweepExpired(at);
|
|
407049
|
+
this.enforceMaxEntries(key);
|
|
407050
|
+
}
|
|
407051
|
+
delete(key) {
|
|
407052
|
+
this.entries.delete(key);
|
|
407053
|
+
}
|
|
407054
|
+
clear() {
|
|
407055
|
+
this.entries.clear();
|
|
407056
|
+
}
|
|
407057
|
+
/** Entries currently retained. The number that used to only go up. */
|
|
407058
|
+
size() {
|
|
407059
|
+
return this.entries.size;
|
|
407060
|
+
}
|
|
407061
|
+
sweepExpired(at) {
|
|
407062
|
+
for (const [key, slot] of this.entries) {
|
|
407063
|
+
if (at - slot.storedAt >= this.ttlMs)
|
|
407064
|
+
this.entries.delete(key);
|
|
407065
|
+
}
|
|
407066
|
+
}
|
|
407067
|
+
/** Evict least-recently-USED first. `protectedKey` is the entry just written —
|
|
407068
|
+
* evicting it would make `set` a no-op. */
|
|
407069
|
+
enforceMaxEntries(protectedKey) {
|
|
407070
|
+
if (this.entries.size <= this.maxEntries)
|
|
407071
|
+
return;
|
|
407072
|
+
const coldestFirst = [...this.entries.entries()].filter(([key]) => key !== protectedKey).toSorted((a, b) => a[1].lastAccessAt - b[1].lastAccessAt);
|
|
407073
|
+
for (const [key] of coldestFirst) {
|
|
407074
|
+
if (this.entries.size <= this.maxEntries)
|
|
407075
|
+
return;
|
|
407076
|
+
this.entries.delete(key);
|
|
407077
|
+
}
|
|
407078
|
+
}
|
|
407079
|
+
};
|
|
407080
|
+
exports.TtlCache = TtlCache;
|
|
407081
|
+
}
|
|
407082
|
+
});
|
|
407083
|
+
|
|
405575
407084
|
// ../../server/backend/dist/core/streaming/stream-probe.service.js
|
|
405576
407085
|
var require_stream_probe_service = __commonJS({
|
|
405577
407086
|
"../../server/backend/dist/core/streaming/stream-probe.service.js"(exports) {
|
|
@@ -405580,16 +407089,28 @@ var require_stream_probe_service = __commonJS({
|
|
|
405580
407089
|
exports.StreamProbeService = void 0;
|
|
405581
407090
|
var child_process_1 = __require("child_process");
|
|
405582
407091
|
var util_1 = __require("util");
|
|
407092
|
+
var ttl_cache_1 = require_ttl_cache();
|
|
405583
407093
|
var types_1 = require_dist9();
|
|
405584
407094
|
var execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
|
|
405585
407095
|
var CACHE_TTL_MS = 36e5;
|
|
405586
407096
|
var PROBE_TIMEOUT_MS = 5e3;
|
|
407097
|
+
var CACHE_MAX_ENTRIES = 512;
|
|
405587
407098
|
var CODEC_ALIASES = {
|
|
405588
407099
|
hevc: "h265"
|
|
405589
407100
|
};
|
|
405590
407101
|
var StreamProbeService = class {
|
|
405591
407102
|
logger;
|
|
405592
|
-
|
|
407103
|
+
/**
|
|
407104
|
+
* Probe results, bounded by BOTH a swept TTL and an entry ceiling.
|
|
407105
|
+
*
|
|
407106
|
+
* It used to be a plain `Map` whose age was consulted only when the same URL
|
|
407107
|
+
* was probed again — so an entry nobody asked about a second time was never
|
|
407108
|
+
* found expired and never removed. See {@link TtlCache}.
|
|
407109
|
+
*/
|
|
407110
|
+
cache = new ttl_cache_1.TtlCache({
|
|
407111
|
+
ttlMs: CACHE_TTL_MS,
|
|
407112
|
+
maxEntries: CACHE_MAX_ENTRIES
|
|
407113
|
+
});
|
|
405593
407114
|
constructor(loggingService) {
|
|
405594
407115
|
this.logger = loggingService.createLogger("StreamProbeService");
|
|
405595
407116
|
}
|
|
@@ -405601,12 +407122,11 @@ var require_stream_probe_service = __commonJS({
|
|
|
405601
407122
|
const force = options?.force ?? false;
|
|
405602
407123
|
if (!force) {
|
|
405603
407124
|
const cached = this.cache.get(url);
|
|
405604
|
-
if (cached
|
|
405605
|
-
return cached
|
|
405606
|
-
}
|
|
407125
|
+
if (cached)
|
|
407126
|
+
return cached;
|
|
405607
407127
|
}
|
|
405608
407128
|
const metadata = await this.runProbe(url);
|
|
405609
|
-
this.cache.set(url,
|
|
407129
|
+
this.cache.set(url, metadata);
|
|
405610
407130
|
return metadata;
|
|
405611
407131
|
}
|
|
405612
407132
|
/**
|
|
@@ -406458,6 +407978,9 @@ var require_manual_boot = __commonJS({
|
|
|
406458
407978
|
var storage_service_1 = require_storage_service();
|
|
406459
407979
|
var stream_probe_service_1 = require_stream_probe_service();
|
|
406460
407980
|
var topology_emitter_service_1 = require_topology_emitter_service();
|
|
407981
|
+
var update_availability_store_js_1 = require_update_availability_store();
|
|
407982
|
+
var agent_installed_packages_js_1 = require_agent_installed_packages();
|
|
407983
|
+
var update_check_scheduler_js_1 = require_update_check_scheduler();
|
|
406461
407984
|
var ServiceContainer = class {
|
|
406462
407985
|
services = /* @__PURE__ */ new Map();
|
|
406463
407986
|
register(ctor, instance) {
|
|
@@ -406503,7 +408026,8 @@ var require_manual_boot = __commonJS({
|
|
|
406503
408026
|
agentRegistryService.setAddonRegistry(addonRegistryService);
|
|
406504
408027
|
agentRegistryService.setClusterNodeHistoryStore(new cluster_node_history_store_1.ClusterNodeHistoryStore(() => capabilityService.getRegistry()?.getSingleton("settings-store") ?? null, loggingService.createLogger("cluster-node-history")));
|
|
406505
408028
|
const addonWidgetsService = new addon_widgets_service_1.AddonWidgetsService(loggingService, capabilityService, addonRegistryService);
|
|
406506
|
-
const
|
|
408029
|
+
const availabilityDataDir = path.resolve(process.env["CAMSTACK_DATA"] ?? "camstack-data");
|
|
408030
|
+
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
408031
|
const backfillLogger = loggingService.createLogger("agent-backfill");
|
|
406508
408032
|
agentRegistryService.setAddonBackfill({
|
|
406509
408033
|
pack: async (name, version) => {
|
|
@@ -406556,10 +408080,40 @@ var require_manual_boot = __commonJS({
|
|
|
406556
408080
|
const serverUpdateService = new server_update_service_1.ServerUpdateService({
|
|
406557
408081
|
logger: loggingService.createLogger("ServerUpdate"),
|
|
406558
408082
|
restartServer: (requestedBy) => addonPackageService.restartServer(requestedBy),
|
|
406559
|
-
eventBus: eventBusService
|
|
408083
|
+
eventBus: eventBusService,
|
|
408084
|
+
updateAvailabilityStore: new update_availability_store_js_1.FileUpdateAvailabilityStore(availabilityDataDir, "server-update", loggingService.createLogger("UpdateAvailability"))
|
|
406560
408085
|
});
|
|
406561
408086
|
const topologyEmitterService = new topology_emitter_service_1.TopologyEmitterService(eventBusService, agentRegistryService, addonRegistryService, (0, cap_providers_1.createNodeRootPackageLookup)(moleculerService, serverUpdateService));
|
|
406562
408087
|
const postBootService = new post_boot_service_1.PostBootService(addonRegistryService, eventBusService, loggingService);
|
|
408088
|
+
const updateCheckLogger = loggingService.createLogger("UpdateCheck");
|
|
408089
|
+
const updateCheckScheduler = new update_check_scheduler_js_1.UpdateCheckScheduler({
|
|
408090
|
+
logger: updateCheckLogger,
|
|
408091
|
+
getIntervalSeconds: () => addonPackageService.getAutoUpdateSettings().updateCheckIntervalSeconds,
|
|
408092
|
+
targets: {
|
|
408093
|
+
listNodes: () => agentRegistryService.listNodeLiveness(),
|
|
408094
|
+
checkHubAddons: () => addonPackageService.checkUpdates(true),
|
|
408095
|
+
checkFrameworkPackages: () => addonPackageService.listFrameworkPackages(),
|
|
408096
|
+
checkAgentAddons: async (nodeId) => {
|
|
408097
|
+
const broker = moleculerService.broker;
|
|
408098
|
+
const installed = await (0, agent_installed_packages_js_1.fetchAgentInstalledPackages)(broker, nodeId);
|
|
408099
|
+
return addonPackageService.checkUpdatesForInstalled(installed, nodeId);
|
|
408100
|
+
},
|
|
408101
|
+
checkNodeServerUpdate: async (nodeId, isHub) => {
|
|
408102
|
+
if (isHub)
|
|
408103
|
+
return serverUpdateService.checkServerUpdate();
|
|
408104
|
+
const proxy = moleculerService.createCapabilityProxy("server-management", nodeId);
|
|
408105
|
+
if (proxy === null) {
|
|
408106
|
+
updateCheckLogger.warn("Update check skipped \u2014 server-management unreachable", {
|
|
408107
|
+
tags: { nodeId }
|
|
408108
|
+
});
|
|
408109
|
+
return void 0;
|
|
408110
|
+
}
|
|
408111
|
+
return proxy["checkServerUpdate"]?.({});
|
|
408112
|
+
}
|
|
408113
|
+
}
|
|
408114
|
+
});
|
|
408115
|
+
addonPackageService.setUpdateCheckRescheduler(() => updateCheckScheduler.reschedule());
|
|
408116
|
+
updateCheckScheduler.start();
|
|
406563
408117
|
const container = new ServiceContainer();
|
|
406564
408118
|
container.register(config_service_1.ConfigService, configService);
|
|
406565
408119
|
container.register(logging_service_1.LoggingService, loggingService);
|
|
@@ -406584,6 +408138,7 @@ var require_manual_boot = __commonJS({
|
|
|
406584
408138
|
container.register(server_update_service_1.ServerUpdateService, serverUpdateService);
|
|
406585
408139
|
container.register(topology_emitter_service_1.TopologyEmitterService, topologyEmitterService);
|
|
406586
408140
|
container.register(post_boot_service_1.PostBootService, postBootService);
|
|
408141
|
+
container.register(update_check_scheduler_js_1.UpdateCheckScheduler, updateCheckScheduler);
|
|
406587
408142
|
const fastify = (0, fastify_1.default)(fastifyOpts);
|
|
406588
408143
|
let closed = false;
|
|
406589
408144
|
let shutdownHooksRegistered = false;
|
|
@@ -406594,6 +408149,11 @@ var require_manual_boot = __commonJS({
|
|
|
406594
408149
|
const logErr = (label, err) => {
|
|
406595
408150
|
console.error(`[manual-boot] ${label} destroy failed:`, err);
|
|
406596
408151
|
};
|
|
408152
|
+
try {
|
|
408153
|
+
updateCheckScheduler.stop();
|
|
408154
|
+
} catch (err) {
|
|
408155
|
+
logErr("UpdateCheckScheduler", err);
|
|
408156
|
+
}
|
|
406597
408157
|
try {
|
|
406598
408158
|
topologyEmitterService.onModuleDestroy();
|
|
406599
408159
|
} catch (err) {
|