@camstack/system 1.2.8 → 1.2.10
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.
- package/dist/addon-runner.js +6 -6
- package/dist/addon-runner.mjs +6 -6
- package/dist/addon-utils.js +1 -1
- package/dist/addon-utils.mjs +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/device-manager/device-manager.addon.js +1 -1
- package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
- package/dist/builtins/local-network/local-network.addon.js +1 -1
- package/dist/builtins/local-network/local-network.addon.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +29 -1
- package/dist/builtins/snapshot/index.mjs +29 -1
- package/dist/builtins/snapshot/snapshot.addon.d.ts +6 -0
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +12 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +48 -7
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +48 -7
- package/dist/builtins/storage-orchestrator/storage-pressure-manager.d.ts +27 -0
- package/dist/builtins/system-config/system-config.addon.js +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{custom-action-registry-BEXwC-oo.mjs → custom-action-registry-F__gp_VX.mjs} +1 -1
- package/dist/{custom-action-registry-vLYEFTtv.js → custom-action-registry-jY0NOZK8.js} +1 -1
- package/dist/{dist-CdgScQ3H.js → dist-BQOCTD5b.js} +1664 -1434
- package/dist/{dist-CxM20CkF.mjs → dist-DpKeSZel.mjs} +1659 -1435
- package/dist/index.js +3 -3
- package/dist/index.mjs +3 -3
- package/dist/kernel/custom-action-registry.d.ts +3 -3
- package/dist/kernel/transport/child-cap-protocol.d.ts +11 -1
- package/dist/{manifest-python-deps-CUHn5wqq.js → manifest-python-deps-1I7WdPDD.js} +5 -3
- package/dist/{manifest-python-deps-CcW0WpfS.mjs → manifest-python-deps-I2MGvDvo.mjs} +5 -3
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $ as storageCapability, Tt as parseJsonObject, d as StorageLocationTypeSchema, ot as BaseAddon } from "../../dist-DpKeSZel.mjs";
|
|
2
2
|
import * as path$1 from "node:path";
|
|
3
3
|
import * as fs from "node:fs/promises";
|
|
4
4
|
import { buildStorageLocationRegistry } from "@camstack/system";
|
|
@@ -607,10 +607,19 @@ var StoragePressureManager = class {
|
|
|
607
607
|
/** Relieve every managed location currently under its threshold. */
|
|
608
608
|
async sweep() {
|
|
609
609
|
const thresholds = await this.deps.getLocationThresholds();
|
|
610
|
+
const relievedDevices = /* @__PURE__ */ new Set();
|
|
610
611
|
for (const [locationId, threshold] of thresholds) {
|
|
611
612
|
if (threshold <= 0) continue;
|
|
613
|
+
const deviceKey = await this.deps.getLocationDeviceKey?.(locationId) ?? null;
|
|
614
|
+
if (deviceKey !== null && relievedDevices.has(deviceKey)) {
|
|
615
|
+
this.deps.logger.debug("storage pressure: skipping location — physical device already relieved this sweep", { meta: {
|
|
616
|
+
locationId,
|
|
617
|
+
deviceKey
|
|
618
|
+
} });
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
612
621
|
try {
|
|
613
|
-
await this.relieveLocation(locationId, threshold);
|
|
622
|
+
if (await this.relieveLocation(locationId, threshold) && deviceKey !== null) relievedDevices.add(deviceKey);
|
|
614
623
|
} catch (err) {
|
|
615
624
|
this.deps.logger.warn("storage pressure: relief failed for location", { meta: {
|
|
616
625
|
locationId,
|
|
@@ -619,16 +628,25 @@ var StoragePressureManager = class {
|
|
|
619
628
|
}
|
|
620
629
|
}
|
|
621
630
|
}
|
|
631
|
+
/**
|
|
632
|
+
* Relieve one location until it recovers, nothing is evictable, or a round
|
|
633
|
+
* makes no progress. Returns `true` when at least one relief round actually
|
|
634
|
+
* reclaimed bytes (so the caller marks the location's physical device relieved
|
|
635
|
+
* for this sweep — the trigger for the over-eviction dedup: freed bytes mean a
|
|
636
|
+
* sibling alias may re-read a not-yet-settled `statfs`), `false` when nothing
|
|
637
|
+
* was freed (never breached, or breached with no evictable data).
|
|
638
|
+
*/
|
|
622
639
|
async relieveLocation(locationId, threshold) {
|
|
623
640
|
const maxRounds = this.deps.maxRounds ?? DEFAULT_MAX_ROUNDS;
|
|
624
641
|
const hysteresis = this.deps.hysteresisPercent ?? DEFAULT_HYSTERESIS_PERCENT;
|
|
642
|
+
let reclaimedAny = false;
|
|
625
643
|
for (let round = 0; round < maxRounds; round++) {
|
|
626
644
|
const free = await this.deps.getFreePercent(locationId);
|
|
627
|
-
if (free >= threshold) return;
|
|
645
|
+
if (free >= threshold) return reclaimedAny;
|
|
628
646
|
const total = await this.deps.getVolumeTotalBytes(locationId);
|
|
629
647
|
const deficitPercent = threshold - free + hysteresis;
|
|
630
648
|
const targetBytes = Math.ceil(total * deficitPercent / 100);
|
|
631
|
-
if (targetBytes <= 0) return;
|
|
649
|
+
if (targetBytes <= 0) return reclaimedAny;
|
|
632
650
|
const providers = this.deps.getEvictableProviders();
|
|
633
651
|
const usages = await Promise.all(providers.map(async (p) => ({
|
|
634
652
|
p,
|
|
@@ -641,7 +659,7 @@ var StoragePressureManager = class {
|
|
|
641
659
|
freePercent: free,
|
|
642
660
|
threshold
|
|
643
661
|
} });
|
|
644
|
-
return;
|
|
662
|
+
return reclaimedAny;
|
|
645
663
|
}
|
|
646
664
|
let reclaimed = 0;
|
|
647
665
|
for (const u of usages) {
|
|
@@ -653,6 +671,7 @@ var StoragePressureManager = class {
|
|
|
653
671
|
});
|
|
654
672
|
reclaimed += res.reclaimedBytes;
|
|
655
673
|
}
|
|
674
|
+
if (reclaimed > 0) reclaimedAny = true;
|
|
656
675
|
this.deps.logger.info("storage pressure: relief round", { meta: {
|
|
657
676
|
locationId,
|
|
658
677
|
round,
|
|
@@ -661,8 +680,9 @@ var StoragePressureManager = class {
|
|
|
661
680
|
targetBytes,
|
|
662
681
|
reclaimedBytes: reclaimed
|
|
663
682
|
} });
|
|
664
|
-
if (reclaimed <= 0) return;
|
|
683
|
+
if (reclaimed <= 0) return reclaimedAny;
|
|
665
684
|
}
|
|
685
|
+
return reclaimedAny;
|
|
666
686
|
}
|
|
667
687
|
};
|
|
668
688
|
//#endregion
|
|
@@ -916,7 +936,8 @@ var StorageOrchestratorAddon = class extends BaseAddon {
|
|
|
916
936
|
getFreePercent: (id) => this.locationFreePercent(id),
|
|
917
937
|
getVolumeTotalBytes: (id) => this.locationVolumeTotalBytes(id),
|
|
918
938
|
getEvictableProviders: () => this.ctx.kernel.capabilityRegistry?.getCollection(STORAGE_EVICTABLE_CAP_NAME) ?? [],
|
|
919
|
-
getLocationThresholds: () => this.resolveLocationThresholds()
|
|
939
|
+
getLocationThresholds: () => this.resolveLocationThresholds(),
|
|
940
|
+
getLocationDeviceKey: (id) => this.locationDeviceKey(id)
|
|
920
941
|
});
|
|
921
942
|
this.pressureTimer = setInterval(() => {
|
|
922
943
|
this.runPressureSweep();
|
|
@@ -1029,6 +1050,26 @@ var StorageOrchestratorAddon = class extends BaseAddon {
|
|
|
1029
1050
|
const bp = (this.service?.listLocations().find((l) => l.id === locationId))?.config["basePath"];
|
|
1030
1051
|
return typeof bp === "string" && bp.length > 0 ? bp : null;
|
|
1031
1052
|
}
|
|
1053
|
+
/**
|
|
1054
|
+
* A stable key for the PHYSICAL storage device behind a location — the
|
|
1055
|
+
* `realpath` of its `basePath`, so symlinked / bind-mounted aliases and
|
|
1056
|
+
* same-root managed locations (`recordings:default` + `recordingsLow:default`,
|
|
1057
|
+
* both at `/recordings`) collapse to one key. Feeds the pressure manager's
|
|
1058
|
+
* in-sweep dedup so a shared disk is relieved at most once per tick (closing
|
|
1059
|
+
* the bounded over-eviction window; see `getLocationDeviceKey`). Falls back to
|
|
1060
|
+
* the lexically-resolved path when realpath throws (root missing at boot),
|
|
1061
|
+
* `null` when the location has no basePath — either way dedup degrades to
|
|
1062
|
+
* per-location, never merging two distinct disks.
|
|
1063
|
+
*/
|
|
1064
|
+
async locationDeviceKey(locationId) {
|
|
1065
|
+
const bp = this.locationBasePath(locationId);
|
|
1066
|
+
if (!bp) return null;
|
|
1067
|
+
try {
|
|
1068
|
+
return await fs.realpath(bp);
|
|
1069
|
+
} catch {
|
|
1070
|
+
return path$1.resolve(bp);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1032
1073
|
/** Free capacity (%) on a location's volume via `statfs`; 100 (guard inert) when unstattable. */
|
|
1033
1074
|
async locationFreePercent(locationId) {
|
|
1034
1075
|
const bp = this.locationBasePath(locationId);
|
|
@@ -28,11 +28,38 @@ export interface PressureManagerDeps {
|
|
|
28
28
|
readonly maxRounds?: number;
|
|
29
29
|
/** Extra headroom % freed beyond the threshold to avoid re-breaching immediately (default 1). */
|
|
30
30
|
readonly hysteresisPercent?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Optional: map a locationId to a stable key identifying its PHYSICAL storage
|
|
33
|
+
* device — the `realpath` of the location's `basePath`, so aliases (symlinks /
|
|
34
|
+
* bind-mounts) and same-root managed locations (`recordings:default` +
|
|
35
|
+
* `recordingsLow:default`, both at `/recordings`) share one key.
|
|
36
|
+
*
|
|
37
|
+
* Powers an in-sweep dedup: once a physical device has actually been RELIEVED
|
|
38
|
+
* this sweep, other locations resolving to the SAME device are skipped for the
|
|
39
|
+
* rest of the tick. This closes a bounded OVER-eviction window — after the
|
|
40
|
+
* first alias frees the shared disk, a second alias re-reading not-yet-settled
|
|
41
|
+
* `statfs` free% (lazy fs accounting, open fds) could see it still breached and
|
|
42
|
+
* evict the oldest footage a second time. A fresh sweep on the next timer tick
|
|
43
|
+
* re-checks the settled free% and finishes any genuinely-residual pressure, so
|
|
44
|
+
* skipping can only ever DEFER relief (self-correcting), never over-delete.
|
|
45
|
+
*
|
|
46
|
+
* Omitted, or returning `null`, → no dedup (each location relieved
|
|
47
|
+
* independently), preserving prior single-disk behaviour.
|
|
48
|
+
*/
|
|
49
|
+
readonly getLocationDeviceKey?: (locationId: string) => Promise<string | null>;
|
|
31
50
|
}
|
|
32
51
|
export declare class StoragePressureManager {
|
|
33
52
|
private readonly deps;
|
|
34
53
|
constructor(deps: PressureManagerDeps);
|
|
35
54
|
/** Relieve every managed location currently under its threshold. */
|
|
36
55
|
sweep(): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Relieve one location until it recovers, nothing is evictable, or a round
|
|
58
|
+
* makes no progress. Returns `true` when at least one relief round actually
|
|
59
|
+
* reclaimed bytes (so the caller marks the location's physical device relieved
|
|
60
|
+
* for this sweep — the trigger for the over-eviction dedup: freed bytes mean a
|
|
61
|
+
* sibling alias may re-read a not-yet-settled `statfs`), `false` when nothing
|
|
62
|
+
* was freed (never breached, or breached with no evictable data).
|
|
63
|
+
*/
|
|
37
64
|
private relieveLocation;
|
|
38
65
|
}
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-BQOCTD5b.js");
|
|
7
7
|
//#region src/builtins/system-config/system-config.addon.ts
|
|
8
8
|
/**
|
|
9
9
|
* Built-in `system-config` addon — Phase 4 of the settings redesign.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Ct as hydrateSchema, at as errMsg, ot as BaseAddon } from "../../dist-DpKeSZel.mjs";
|
|
2
2
|
//#region src/builtins/system-config/system-config.addon.ts
|
|
3
3
|
/**
|
|
4
4
|
* Built-in `system-config` addon — Phase 4 of the settings redesign.
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-BQOCTD5b.js");
|
|
7
7
|
const require_formatter = require("../../formatter-DqAKDlvN.js");
|
|
8
8
|
let node_path = require("node:path");
|
|
9
9
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ot as BaseAddon, z as logDestinationCapability } from "../../dist-DpKeSZel.mjs";
|
|
2
2
|
import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
|
|
3
3
|
import * as path$1 from "node:path";
|
|
4
4
|
import path from "node:path";
|
|
@@ -16,7 +16,7 @@ var CustomActionRegistry = class {
|
|
|
16
16
|
if (scope.kind !== "system") throw new Error(`custom action '${addonId}.${name}' declared scope '${scope.kind}' — not yet implemented`);
|
|
17
17
|
actions.set(name, {
|
|
18
18
|
spec,
|
|
19
|
-
handler: (input) => handler(name, input)
|
|
19
|
+
handler: (input, caller) => handler(name, input, caller)
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
this.byAddon.set(addonId, actions);
|
|
@@ -16,7 +16,7 @@ var CustomActionRegistry = class {
|
|
|
16
16
|
if (scope.kind !== "system") throw new Error(`custom action '${addonId}.${name}' declared scope '${scope.kind}' — not yet implemented`);
|
|
17
17
|
actions.set(name, {
|
|
18
18
|
spec,
|
|
19
|
-
handler: (input) => handler(name, input)
|
|
19
|
+
handler: (input, caller) => handler(name, input, caller)
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
this.byAddon.set(addonId, actions);
|